keel/util/version/version.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()
}