Skip to content

Fix package-version dropdown hiding fresh versions behind lex-sort + take

Summary

  • Real bug: a user pushed Docker image 1.1.0 after a long history of 1.0.x-N tags; the Create-Release dropdown showed 30 versions ending at 1.0.3-8 and 1.1.0 was missing. Docker registries return tags in lexicographic order, where 1.0.3-8 sorts before 1.1.0 ('0' < '1' at the third character). The strategy was truncating to take=30 BEFORE PackageVersionFilter sorted by semver — fresh versions past the lex-take were invisible.
  • Fix: IPackageVersionStrategy.ListVersionsAsync no longer takes a take parameter. Strategies return ALL upstream versions (with pagination + sanity cap); PackageVersionFilter.Apply becomes the single point of filter + semver sort + take.
  • Scaling: Docker + GitHub now follow RFC 5988 Link: rel="next" headers across pages; Helm is single-file (no pagination needed). All three strategies enforce a 5000-default enumeration cap to protect against pathological feeds, with SQUID_PACKAGE_VERSION_MAX_ENUMERATE env-var override (Rule 8).

Changes

File Change
IPackageVersionStrategy.cs Drop take param; doc-comment explains the production bug it fixed
DockerPackageVersionStrategy.cs Pagination loop with sticky bearer-token upgrade on 401 — ?n=100 per page, follow Link header
GitHubPackageVersionStrategy.cs ?per_page=100 (was ?per_page={take}) + Link-header pagination loop
HelmPackageVersionStrategy.cs Drop pre-sort take; cap parameter is now a sanity ceiling only
ExternalFeedPackageVersionService.cs Calls strategy without take; passes take only to PackageVersionFilter.Apply
LinkHeaderParser.cs (new) Pure RFC 5988 parser — GitHub absolute + Docker relative URLs, case-insensitive rel, quoted + bare values
PackageVersionEnumerationCap.cs (new) SQUID_PACKAGE_VERSION_MAX_ENUMERATE env-var-pinnable cap (default 5000)

Test plan

  • Unit: 5108/5108 pass (dotnet test tests/Squid.UnitTests/)
  • User bug pinned: ListVersionsAsync_ShouldNotHideNewerVersionBehindLexSort (32 lex-ordered Docker tags ending at 1.1.0 → result top-of-list is 1.1.0)
  • Drift pins: MaxTagsPerPage/MaxReleasesPerPage (100), PackageVersionEnumerationCap.Default (5000), MaxItemsEnvVar literal (Rule 8)
  • LinkHeaderParser: 13 cases covering both URL styles + edge formats
  • Strategy parsers inverted: each ShouldRespectTakeLimit test now ShouldReturnAllTagsFromPage_NoTruncation so re-introducing pre-sort truncation fails loudly
  • Manual: verify the dropdown shows 1.1.0 at top of the user's actual web feed in staging post-deploy

Rollout

git revert is clean — no schema or DI surface changed. The contract change is internal-only (IPackageVersionStrategy); the public IExternalFeedPackageVersionService.ListVersionsAsync signature is unchanged so request handlers + callers are untouched.

🤖 Generated with Claude Code

Merge request reports

Loading