-
released this
2026-04-25 11:59:28 -04:00 | 140 commits to main since this releaseSummary
Fixes #1081. When the version flagged
is_latestwas soft-deleted, no
other version was promoted, and becauseGetCurrentLatestVersiondidn't
filter deleted rows, subsequent publishes with a lower semver never
became latest — leaving/v0/servers/{name}/versions/latestpermanently
404.This PR has two parts:
1. Code fix — recalculate
is_lateston every status change- Recalculate
is_latestafter every status mutation
(UpdateServerStatus,UpdateAllVersionsStatus, and the inline status
change inUpdateServer), in the same transaction that already holds
the per-server advisory lock. - Winner selection reuses the existing
CompareVersionslogic: highest
non-deleted version gets the flag; if every version is deleted, the
highest deleted version keeps it so admin lookups via
includeDeleted=truestill resolve the server (important for restore
flows). - New
SetLatestVersionDB primitive issues two statements (clear old,
set new) —idx_unique_latest_per_serveris a non-deferrable unique
partial index, so a singleUPDATEthat flips two rows trips the
constraint.
2. One-shot migration — heal servers already affected (no manual SQL
needed)
014_heal_is_latest.sqlpromotes the highest non-deleted version on
every server whoseis_latestflag is on a deleted row (or absent
entirely). Picker parsesmajor.minor.patchnumerically and falls back
topublished_atfor non-semver or as a tiebreaker. Heals
io.github.containers/kubernetes-mcp-serverto0.0.61automatically
and catches any other servers silently affected by the original bug,
without hardcoded version numbers.Picker behavior vs.
CompareVersions:- Standard
M.m.psemver: matches exactly, including the
backport-after-delete case (e.g. published 1.0.1 then 1.0.0 hotfix after
deleting 2.0.0 → picks 1.0.1, not 1.0.0). - Non-semver: falls through to
published_at DESC. Reasonable. - Prereleases (
1.0.0-alphavs1.0.0): both parse to(1,0,0),
tiebreak bypublished_at. Diverges fromCompareVersions(which
always says1.0.0 > 1.0.0-alpha). Edge case requires (a) server hit by
this bug AND (b) prereleases at the top of the version list. Acceptable;
the publisher can flip it with onemcp-publisher statuscall (which
now triggers proper recalc thanks to part 1).
Verification
End-to-end curl repro of the exact scenario from the issue, run against
bothmainand this branch:Before (main):
publish 1.0.0 → 200 publish 0.0.59 → 200 PATCH .../1.0.0/status {deleted} → 200 GET /versions/latest → 404 ← bug publish 0.0.60 → 200 GET /versions/latest → 404 ← bug persists, 0.0.60 has isLatest=FalseAfter (this branch):
publish 1.0.0 → 200 publish 0.0.59 → 200 PATCH .../1.0.0/status {deleted} → 200 GET /versions/latest → 200 (0.0.59, isLatest=true) publish 0.0.60 → 200 GET /versions/latest → 200 (0.0.60, isLatest=true)Test plan
- Service tests covering: soft-delete latest → next highest active
promoted; the exact #1081 repro end-to-end through the service;
all-versions-deleted still keeps highest addressable for admin;
restore-to-higher-version reclaims latest. - Migration tests covering: kubernetes-mcp-server scenario (heals to
highest semver); backport scenario (semver beats most-recent-published);
no-is_latest-row defensive case; all-deleted server left untouched;
healthy server left untouched; non-semver versions fall back to
published_at. - Full unit test suite (
./internal/... ./cmd/...) passes against a
real Postgres. make lint(0 issues) andmake validatepass.- Manual curl repro against a running registry confirms the fix
resolves the failure mode andmainstill reproduces it.
🤖 Generated with Claude Code
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Recalculate