159 lines
3.5 KiB
Go
159 lines
3.5 KiB
Go
package version
|
|
|
|
import (
|
|
"errors"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/Masterminds/semver"
|
|
"github.com/keel-hq/keel/types"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ErrVersionTagMissing - tag missing error
|
|
var ErrVersionTagMissing = errors.New("version tag is missing")
|
|
|
|
// ErrInvalidSemVer is returned a version is found to be invalid when
|
|
// being parsed.
|
|
var ErrInvalidSemVer = errors.New("invalid semantic version")
|
|
var ErrNoMajorMinorPatchElementsFound = errors.New("No Major.Minor.Patch elements found")
|
|
|
|
// MustParse - must parse version, if fails - panics
|
|
func MustParse(version string) *types.Version {
|
|
ver, err := GetVersion(version)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ver
|
|
}
|
|
|
|
// GetVersion - parse version
|
|
func GetVersion(version string) (*types.Version, error) {
|
|
|
|
parts := strings.SplitN(version, ".", 3)
|
|
if len(parts) != 2 && len(parts) != 3 {
|
|
return nil, ErrNoMajorMinorPatchElementsFound
|
|
}
|
|
|
|
v, err := semver.NewVersion(version)
|
|
if err != nil {
|
|
if err == semver.ErrInvalidSemVer {
|
|
return nil, ErrInvalidSemVer
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return &types.Version{
|
|
Major: v.Major(),
|
|
Minor: v.Minor(),
|
|
Patch: v.Patch(),
|
|
PreRelease: string(v.Prerelease()),
|
|
Metadata: v.Metadata(),
|
|
Original: v.Original(),
|
|
}, nil
|
|
}
|
|
|
|
// GetVersionFromImageName - get version from image name
|
|
func GetVersionFromImageName(name string) (*types.Version, error) {
|
|
parts := strings.Split(name, ":")
|
|
if len(parts) > 1 {
|
|
return GetVersion(parts[1])
|
|
}
|
|
|
|
return nil, ErrVersionTagMissing
|
|
}
|
|
|
|
// GetImageNameAndVersion - get name and version
|
|
func GetImageNameAndVersion(name string) (string, *types.Version, error) {
|
|
parts := strings.Split(name, ":")
|
|
if len(parts) > 0 {
|
|
v, err := GetVersion(parts[1])
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
return parts[0], v, nil
|
|
}
|
|
|
|
return "", nil, ErrVersionTagMissing
|
|
}
|
|
|
|
// NewAvailable - takes version and current tags. Checks whether there is a new version in the list of tags
|
|
// and returns it as well as newAvailable bool
|
|
func NewAvailable(current string, tags []string, matchPreRelease bool) (newVersion string, newAvailable bool, err error) {
|
|
|
|
currentVersion, err := semver.NewVersion(current)
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
if len(tags) == 0 {
|
|
return "", false, nil
|
|
}
|
|
|
|
var vs []*semver.Version
|
|
for _, r := range tags {
|
|
v, err := semver.NewVersion(r)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"error": err,
|
|
"tag": r,
|
|
}).Debug("failed to parse tag")
|
|
continue
|
|
|
|
}
|
|
|
|
if matchPreRelease && currentVersion.Prerelease() != v.Prerelease() {
|
|
continue
|
|
}
|
|
|
|
vs = append(vs, v)
|
|
}
|
|
|
|
if len(vs) == 0 {
|
|
log.Debug("no versions available")
|
|
return "", false, nil
|
|
}
|
|
|
|
sort.Sort(sort.Reverse(semver.Collection(vs)))
|
|
|
|
if currentVersion.LessThan(vs[0]) {
|
|
log.WithFields(log.Fields{"currentVersion": currentVersion, "latestAvailable": vs[0]}).Debug("latest available is newer than current")
|
|
return vs[0].Original(), true, nil
|
|
}
|
|
log.WithFields(log.Fields{"currentVersion": currentVersion, "latestAvailable": vs[0]}).Debug("latest available is not newer than current")
|
|
return "", false, nil
|
|
}
|
|
|
|
// Lowest - returns the lowest versioned tag from the slice
|
|
func Lowest(tags []string) string {
|
|
if len(tags) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var vs []*semver.Version
|
|
for _, r := range tags {
|
|
v, err := semver.NewVersion(r)
|
|
if err != nil {
|
|
continue
|
|
|
|
}
|
|
|
|
if v.Prerelease() != "" {
|
|
continue
|
|
}
|
|
|
|
vs = append(vs, v)
|
|
}
|
|
|
|
if len(vs) == 0 {
|
|
log.Debug("no versions available")
|
|
return ""
|
|
}
|
|
|
|
sort.Sort(semver.Collection(vs))
|
|
|
|
return vs[0].String()
|
|
}
|