fix(autodiscovery/dockercompose): skip images whose tag is not a version - #9760
Merged
Merged
Conversation
NewDockerImageSpecFromImage returns nil when the tag cannot be recognized as a version, so sourceSpec is nil for any ':latest' or variant-suffixed tag. Two bugs follow from that, both from inlining code the dockerfile crawler keeps in a per-image function where `return nil, nil` means "skip this image". Writing the fallback pattern to sourceSpec.VersionFilter.Pattern dereferences that nil whenever the crawler also carries a versionfilter, since GreaterThanPattern fails on the very same tag. dockerfile.go assigns the local versionFilterPattern instead, which is nil-safe and is also the variable the template actually reads. The `return nil, nil` in the template selection ends the whole crawl instead of skipping one service, discarding every manifest found so far. A single ':latest' service made the crawler emit no pipeline at all for the repository, exit 0, and say nothing above Info level. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Member
|
Thanks @jouve for the fix, could I ask you to fix the two typos? |
olblak
approved these changes
Aug 2, 2026
olblak
left a comment
Member
There was a problem hiding this comment.
Thank you for the fix and the detailed explaination.
I confirm that your pr fix a real issue
|
Tick the box to add this pull request to the merge queue (same as
|
olblak
enabled auto-merge (squash)
August 2, 2026 17:16
Contributor
Author
|
thanks for quick review ! |
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.
No existing issue — happy to open one if you'd prefer the usual
Fix #XXXreference.NewDockerImageSpecFromImagereturnsnilwhen the tag cannot be recognized as a version (dockerimage/spec.go:103), sosourceSpecis nil for any:latestor variant-suffixed tag. Two bugs follow, both from inlining code that thedockerfilecrawler keeps in a per-image function, wherereturn nil, nilmeans "skip this image".1. Nil dereference —
compose.go:164The two functions choke on the same input, so they always fail together: a tag that
getTagFilterFromValuedoesn't recognize is also a tagsv.NewVersionrefuses. Whenever this branch is entered,sourceSpecis nil.The write is also dead even when it isn't nil — nothing reads
sourceSpec.VersionFilterafter the block atcompose.go:144-154. What reaches the template is the localversionFilterPattern, which at that point holds the empty string returned by the failed call.dockerfile.go:167assigns that local, which is both nil-safe and the value actually rendered — this PR does the same.Reproducer, panics on v0.119.0 and on main:
addr=0x90is consistent with a nil base plus the field offset:unsafe.OffsetofputsSpec.VersionFilter.Patternat 136 (0x88), and astringassignment stores two words, so the fault surfaces on the second one.2. The whole crawl is abandoned —
compose.go:190In the template selection,
sourceSpec == nilwithdigest: falsereachesreturn nil, nil. Insidefor _, svc := range svcListthat discards themanifestsaccumulated so far and ends discovery for every remaining compose file, rather than skipping one service.A single
:latestservice therefore makes the crawler emit no pipeline at all for the repository, exit 0, and say nothing above Info level. This is how I hit it: a compose file whose images were all pinned except one stopped being watched entirely, silently.Reproducer — no
versionfilter, so the panic above is not reached:Test
Scenario 3inmain_test.gocovers both: the fixture lists the unparseable-tagged service after the pinned one, so the assertion on a single expected pipeline fails if the accumulated manifest is dropped. It needs its owntestdata-unparseable-tag/directory becausesearchDockerComposeFileswalks recursively, and a fixture undertestdata/would change the expectations of scenarios 1 and 2.Verified that the test fails without the fix —
panicinsideTestDiscoverManifests/Scenario_3, atcompose.go:164— and passes with it.gofmt -landgo vetare clean on the package.Additional Information
Checklist
Tradeoff
Keeping the
versionFilterPattern = "*"fallback rather than deleting the branch is arguably belt-and-braces: in the common case the image is skipped a few lines later anyway, so the value is discarded. It still matters for degenerate tags such as1.—^\d*(\.\d*){1}$matches it since\d*accepts the empty string, so the spec is non-nil, whilesv.NewVersion("1.")fails — and it keeps the two crawlers symmetrical.Potential improvement
matchingRule.go:39declaresruleResultsoutside thefor _, matchingRule := range mloop and never resets it, so a rule that follows a non-matching one can never match. Present in thedockercompose,dockerfileand other copies of that helper. Out of scope here; happy to send a separate PR if you want it fixed.Co-authored with Claude Opus 5.