53 lines
1.7 KiB
Go
53 lines
1.7 KiB
Go
package patchruntime
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/distribution/reference"
|
|
"golang.org/x/mod/semver"
|
|
)
|
|
|
|
// errImageRefNoTag reports that an image reference does not declare a
|
|
// tag. The patch service maps it to `image_ref_not_semver` because a
|
|
// digest-only or tagless reference cannot carry a semver-comparable
|
|
// version.
|
|
var errImageRefNoTag = errors.New("image reference is missing a tag")
|
|
|
|
// extractSemverTag returns the canonical semver string ("v1.4.7") for
|
|
// imageRef, ready to feed into golang.org/x/mod/semver. The leading "v"
|
|
// is added when the underlying tag omits it.
|
|
//
|
|
// Errors returned by this function are pre-formatted for inclusion in
|
|
// the patch service's `image_ref_not_semver` failure message.
|
|
func extractSemverTag(imageRef string) (string, error) {
|
|
parsed, err := reference.ParseNormalizedNamed(imageRef)
|
|
if err != nil {
|
|
return "", fmt.Errorf("parse image reference %q: %w", imageRef, err)
|
|
}
|
|
tagged, ok := parsed.(reference.NamedTagged)
|
|
if !ok {
|
|
return "", fmt.Errorf("%w: %q", errImageRefNoTag, imageRef)
|
|
}
|
|
tag := strings.TrimSpace(tagged.Tag())
|
|
if tag == "" {
|
|
return "", fmt.Errorf("%w: %q", errImageRefNoTag, imageRef)
|
|
}
|
|
candidate := tag
|
|
if !strings.HasPrefix(candidate, "v") {
|
|
candidate = "v" + candidate
|
|
}
|
|
if !semver.IsValid(candidate) {
|
|
return "", fmt.Errorf("tag %q on image reference %q is not a valid semver", tag, imageRef)
|
|
}
|
|
return candidate, nil
|
|
}
|
|
|
|
// samePatchSeries reports whether two canonical semver strings (with
|
|
// the leading "v") share their major and minor components. The third
|
|
// component (patch) and any pre-release / build metadata are ignored.
|
|
func samePatchSeries(currentSemver, newSemver string) bool {
|
|
return semver.MajorMinor(currentSemver) == semver.MajorMinor(newSemver)
|
|
}
|