fix(registry): paginate Docker Hub tags so newest release is found - #8
Merged
Conversation
Docker Hub serves tags ordered by last_updated, which is not semver order. Reading only the first page (50 tags) of repos with thousands of tags (harmony, bsc, klaytn) hid the real latest release, so versioncheck reported a stale "latest" (harmony saw v4.2.1 instead of v8.x, bsc 1.3.0 instead of 1.6.x). Now follow the response "next" link with page_size=100, accumulating matching tags across pages until a generous surplus (~maxResults*4) is held or pages run out, capped at maxPages=20 to avoid an unbounded crawl on giant repos. The caller still picks the semver max via IsNewer, now over a much larger candidate set, so last_updated ordering no longer matters. Context: I traced how only page 1 was parsed and reproduced the stale-latest behaviour, confirmed last_updated is not semver order from the API shape, then added pagination plus httptest-based tests (rewriteTransport) covering multi-page collection, the surplus stop, the maxPages cap, and ctx cancellation. Spent ~1.5h investigating and testing.
tazhate
added a commit
that referenced
this pull request
Jun 14, 2026
…ub tests Merging the GAR (#6) and Docker Hub pagination (#8) branches landed two copies of rewriteTransport and tagSet in package registry with different signatures, so the test binary stopped compiling (the merges only ever got go-built, which skips _test.go). Renamed the oci_test.go pair to ociRewriteTransport / ociTagList to clear the collision. Context: caught this during a local merge-train of the seven open PRs — go build passed after each merge but go test -count=1 failed the registry package on duplicate decls. Reconciled by hand, full suite green after.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
internal/registry/dockerhub.goonly read the first page (50 tags) of the Docker Hub tags API withordering=last_updated. Becauselast_updatedis not semver order, repositories with thousands of tags never surfaced their real latest release on page 1, soversioncheckreported a stale "latest".This change follows the response
nextlink and accumulates matching tags across pages (page_size=100) until it holds a generous surplus (~maxResults*4) or pages run out, capped atmaxPages=20to avoid unbounded crawls on giant repos. TheLatestTags(ctx, policy, maxResults)signature, pattern filtering, and error behavior are unchanged. The caller still picks the semver max viaregistry.IsNewer, now over a much larger candidate set.Before / After
Implementation notes
Next string(json:"next") todockerHubTagsResponse.resolveDockerHubNextfollows the absolutenextURL as-is; relative links resolve against the current page URL.ctxis checked on every page (cancellation / timeout respected).Test plan
go build ./...andgo test ./internal/registry/...are green. New tests ininternal/registry/dockerhub_test.gousinghttptest.Server+ arewriteTransport(mirrors the oci_test pattern):nextURLs; asserts all pages fetched, matching tags collected from every page, non-matching tags filtered, and semver selection finds the release on the deepest page.nextchain; stops at exactly maxPages fetches.