in container_images/registry-image-forked/commands/check.go [110:274]
func checkRepository(repo name.Repository, source resource.Source, from *resource.Version, opts ...remote.Option) (resource.CheckResponse, error) {
tags, err := remote.List(repo, opts...)
if err != nil {
return resource.CheckResponse{}, fmt.Errorf("list repository tags: %w", err)
}
bareTag := "latest"
if source.Variant != "" {
bareTag = source.Variant
}
versionTags := map[*semver.Version]name.Tag{}
tagDigests := map[string]string{}
digestVersions := map[string]*semver.Version{}
var cursorVer *semver.Version
var latestTag string
if from != nil {
// assess the 'from' tag first so we can skip lower version numbers
sort.Slice(tags, func(i, j int) bool {
return tags[i] == from.Tag
})
}
var constraint *semver.Constraints
if source.SemverConstraint != "" {
constraint, err = semver.NewConstraint(source.SemverConstraint)
if err != nil {
return resource.CheckResponse{}, fmt.Errorf("parse semver constraint: %w", err)
}
}
for _, identifier := range tags {
var ver *semver.Version
if identifier == bareTag {
latestTag = identifier
} else {
verStr := identifier
if source.Variant != "" {
if !strings.HasSuffix(identifier, "-"+source.Variant) {
continue
}
verStr = strings.TrimSuffix(identifier, "-"+source.Variant)
}
ver, err = semver.NewVersion(verStr)
if err != nil {
// not a version
continue
}
if constraint != nil && !constraint.Check(ver) {
// semver constraint not met
continue
}
pre := ver.Prerelease()
if pre != "" {
// pre-releases not enabled; skip
if !source.PreReleases {
continue
}
// contains additional variant
if strings.Contains(pre, "-") {
continue
}
if !strings.HasPrefix(pre, "alpha") &&
!strings.HasPrefix(pre, "beta") &&
!strings.HasPrefix(pre, "rc") {
// additional variant, not a prerelease segment
continue
}
}
if cursorVer != nil && (cursorVer.GreaterThan(ver) || cursorVer.Equal(ver)) {
// optimization: don't bother fetching digests for lesser (or equal but
// less specific, i.e. 6.3 vs 6.3.0) version tags
continue
}
}
tagRef := repo.Tag(identifier)
digest, found, err := headOrGet(tagRef, opts...)
if err != nil {
return resource.CheckResponse{}, fmt.Errorf("get tag digest: %w", err)
}
if !found {
continue
}
tagDigests[identifier] = digest.String()
if ver != nil {
versionTags[ver] = tagRef
existing, found := digestVersions[digest.String()]
shouldSet := !found
if found {
if existing.Prerelease() == "" && ver.Prerelease() != "" {
// favor final version over prereleases
shouldSet = false
} else if existing.Prerelease() != "" && ver.Prerelease() == "" {
// favor final version over prereleases
shouldSet = true
} else if strings.Count(ver.Original(), ".") > strings.Count(existing.Original(), ".") {
// favor more specific semver tag (i.e. 3.2.1 over 3.2, 1.0.0-rc.2 over 1.0.0-rc)
shouldSet = true
}
}
if shouldSet {
digestVersions[digest.String()] = ver
}
}
if from != nil && identifier == from.Tag && digest.String() == from.Digest {
// if the 'from' version exists and has the same digest, treat its
// version as a cursor in the tags, only considering newer versions
//
// note: the 'from' version will always be the first one hit by this loop
cursorVer = ver
}
}
var tagVersions TagVersions
for digest, version := range digestVersions {
tagVersions = append(tagVersions, TagVersion{
TagName: versionTags[version].TagStr(),
Digest: digest,
Version: version,
})
}
sort.Sort(tagVersions)
response := resource.CheckResponse{}
for _, ver := range tagVersions {
response = append(response, resource.Version{
Tag: ver.TagName,
Digest: ver.Digest,
})
}
if latestTag != "" {
digest := tagDigests[latestTag]
_, existsAsSemver := digestVersions[digest]
if !existsAsSemver && constraint == nil {
response = append(response, resource.Version{
Tag: latestTag,
Digest: digest,
})
}
}
return response, nil
}