Review version comparision logic

This commit is contained in:
Daniele Rondina
2019-12-27 11:18:52 +01:00
committed by Ettore Di Giacinto
parent bbeb800611
commit 91d05b071d
4 changed files with 457 additions and 22 deletions

View File

@@ -20,7 +20,6 @@ import (
"encoding/json"
"sync"
version "github.com/hashicorp/go-version"
"github.com/pkg/errors"
)
@@ -178,16 +177,11 @@ func (db *InMemoryDatabase) getProvide(p Package) (Package, error) {
for ve, _ := range versions {
v, err := version.NewVersion(p.GetVersion())
match, err := p.VersionMatchSelector(ve)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "Error on match version")
}
constraints, err := version.NewConstraint(ve)
if err != nil {
return nil, err
}
if constraints.Check(v) {
if match {
pa, ok := db.ProvidesDatabase[p.GetPackageName()][ve]
if !ok {
return nil, errors.New("No versions found for package")
@@ -257,15 +251,12 @@ func (db *InMemoryDatabase) FindPackages(p Package) ([]Package, error) {
}
var versionsInWorld []Package
for ve, _ := range versions {
v, err := version.NewVersion(ve)
match, err := p.SelectorMatchVersion(ve)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "Error on match selector")
}
constraints, err := version.NewConstraint(p.GetVersion())
if err != nil {
return nil, err
}
if constraints.Check(v) {
if match {
w, err := db.FindPackage(&DefaultPackage{Name: p.GetName(), Category: p.GetCategory(), Version: ve})
if err != nil {
return nil, errors.Wrap(err, "Cache mismatch - this shouldn't happen")

View File

@@ -83,6 +83,8 @@ type Package interface {
GetLicense() string
IsSelector() bool
VersionMatchSelector(string) (bool, error)
SelectorMatchVersion(string) (bool, error)
}
type Tree interface {
@@ -322,15 +324,11 @@ func (p *DefaultPackage) Expand(definitiondb PackageDatabase) ([]Package, error)
return nil, err
}
for _, w := range all {
v, err := version.NewVersion(w.GetVersion())
match, err := p.SelectorMatchVersion(w.GetVersion())
if err != nil {
return nil, err
}
constraints, err := version.NewConstraint(p.GetVersion())
if err != nil {
return nil, err
}
if constraints.Check(v) {
if match {
versionsInWorld = append(versionsInWorld, w)
}
}

262
pkg/package/version.go Normal file
View File

@@ -0,0 +1,262 @@
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>,
// Daniele Rondina <geaaru@sabayonlinux.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package pkg
import (
"errors"
"fmt"
"regexp"
"strings"
version "github.com/hashicorp/go-version"
)
// Package Selector Condition
type PkgSelectorCondition int
type PkgVersionSelector struct {
Version string
VersionSuffix string
Condition PkgSelectorCondition
// TODO: Integrate support for multiple repository
}
const (
PkgCondInvalid = 0
// >
PkgCondGreater = 1
// >=
PkgCondGreaterEqual = 2
// <
PkgCondLess = 3
// <=
PkgCondLessEqual = 4
// =
PkgCondEqual = 5
// !
PkgCondNot = 6
// ~
PkgCondAnyRevision = 7
// =<pkg>*
PkgCondMatchVersion = 8
)
func ParseVersion(v string) (PkgVersionSelector, error) {
var ans PkgVersionSelector = PkgVersionSelector{
Version: "",
VersionSuffix: "",
Condition: PkgCondInvalid,
}
if strings.HasPrefix(v, ">=") {
v = v[2:]
ans.Condition = PkgCondGreaterEqual
} else if strings.HasPrefix(v, ">") {
v = v[1:]
ans.Condition = PkgCondGreater
} else if strings.HasPrefix(v, "<=") {
v = v[2:]
ans.Condition = PkgCondLessEqual
} else if strings.HasPrefix(v, "<") {
v = v[1:]
ans.Condition = PkgCondLess
} else if strings.HasPrefix(v, "=") {
v = v[1:]
if strings.HasSuffix(v, "*") {
ans.Condition = PkgCondMatchVersion
v = v[0 : len(v)-1]
} else {
ans.Condition = PkgCondEqual
}
} else if strings.HasPrefix(v, "~") {
v = v[1:]
ans.Condition = PkgCondAnyRevision
} else if strings.HasPrefix(v, "!") {
v = v[1:]
ans.Condition = PkgCondNot
}
regexPkg := regexp.MustCompile(
fmt.Sprintf("(%s|%s|%s|%s|%s|%s)((%s|%s|%s|%s|%s|%s|%s)+)*$",
// Version regex
// 1.1
"[0-9]+[.][0-9]+[a-z]*",
// 1
"[0-9]+[a-z]*",
// 1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// 1.1.1.1.1.1
"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[a-z]*",
// suffix
"-r[0-9]+",
"_p[0-9]+",
"_pre[0-9]*",
"_rc[0-9]+",
// handle also rc without number
"_rc",
"_alpha",
"_beta",
),
)
matches := regexPkg.FindAllString(v, -1)
if len(matches) > 0 {
// Check if there patch
if strings.Contains(matches[0], "_p") {
ans.Version = matches[0][0:strings.Index(matches[0], "_p")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "_p"):]
} else if strings.Contains(matches[0], "_rc") {
ans.Version = matches[0][0:strings.Index(matches[0], "_rc")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "_rc"):]
} else if strings.Contains(matches[0], "_alpha") {
ans.Version = matches[0][0:strings.Index(matches[0], "_alpha")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "_alpha"):]
} else if strings.Contains(matches[0], "_beta") {
ans.Version = matches[0][0:strings.Index(matches[0], "_beta")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "_beta"):]
} else if strings.Contains(matches[0], "-r") {
ans.Version = matches[0][0:strings.Index(matches[0], "-r")]
ans.VersionSuffix = matches[0][strings.Index(matches[0], "-r"):]
} else {
ans.Version = matches[0]
}
}
// Set condition if there isn't a prefix but only a version
if ans.Condition == PkgCondInvalid && ans.Version != "" {
ans.Condition = PkgCondEqual
}
// NOTE: Now suffix complex like _alpha_rc1 are not supported.
return ans, nil
}
func PackageAdmit(selector, i PkgVersionSelector) (bool, error) {
var v1 *version.Version = nil
var v2 *version.Version = nil
var ans bool
var err error
if selector.Version != "" {
v1, err = version.NewVersion(selector.Version)
if err != nil {
return false, err
}
}
if i.Version != "" {
v2, err = version.NewVersion(i.Version)
if err != nil {
return false, err
}
} else {
// If version is not defined match always package
ans = true
}
// If package doesn't define version admit all versions of the package.
if selector.Version == "" {
ans = true
} else {
if selector.Condition == PkgCondInvalid || selector.Condition == PkgCondEqual {
// case 1: source-pkg-1.0 and dest-pkg-1.0 or dest-pkg without version
if i.Version != "" && i.Version == selector.Version && selector.VersionSuffix == i.VersionSuffix {
ans = true
}
} else if selector.Condition == PkgCondAnyRevision {
if v1 != nil && v2 != nil {
ans = v1.Equal(v2)
}
} else if selector.Condition == PkgCondMatchVersion {
// TODO: case of 7.3* where 7.30 is accepted.
if v1 != nil && v2 != nil {
segments := v1.Segments()
n := strings.Count(selector.Version, ".")
switch n {
case 0:
segments[0]++
case 1:
segments[1]++
case 2:
segments[2]++
default:
segments[len(segments)-1]++
}
nextVersion := strings.Trim(strings.Replace(fmt.Sprint(segments), " ", ".", -1), "[]")
constraints, err := version.NewConstraint(
fmt.Sprintf(">= %s, < %s", selector.Version, nextVersion),
)
if err != nil {
return false, err
}
ans = constraints.Check(v2)
}
} else if v1 != nil && v2 != nil {
// TODO: Integrate check of version suffix
switch selector.Condition {
case PkgCondGreaterEqual:
ans = v2.GreaterThanOrEqual(v1)
case PkgCondLessEqual:
ans = v2.LessThanOrEqual(v1)
case PkgCondGreater:
ans = v2.GreaterThan(v1)
case PkgCondLess:
ans = v2.LessThan(v1)
case PkgCondNot:
ans = !v2.Equal(v1)
}
}
}
return ans, nil
}
func (p *DefaultPackage) SelectorMatchVersion(v string) (bool, error) {
if !p.IsSelector() {
return false, errors.New("Package is not a selector")
}
vS, err := ParseVersion(p.GetVersion())
if err != nil {
return false, err
}
vSI, err := ParseVersion(v)
if err != nil {
return false, err
}
return PackageAdmit(vS, vSI)
}
func (p *DefaultPackage) VersionMatchSelector(selector string) (bool, error) {
vS, err := ParseVersion(selector)
if err != nil {
return false, err
}
vSI, err := ParseVersion(p.GetVersion())
if err != nil {
return false, err
}
return PackageAdmit(vS, vSI)
}

184
pkg/package/version_test.go Normal file
View File

@@ -0,0 +1,184 @@
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
// Daniele Rondina <geaaru@sabayonlinux.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package pkg_test
import (
. "github.com/mudler/luet/pkg/package"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Versions", func() {
Context("Versions Parser1", func() {
v, err := ParseVersion(">=1.0")
It("ParseVersion1", func() {
var c PkgSelectorCondition = PkgCondGreaterEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("1.0"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser2", func() {
v, err := ParseVersion(">1.0")
It("ParseVersion2", func() {
var c PkgSelectorCondition = PkgCondGreater
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("1.0"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser3", func() {
v, err := ParseVersion("<=1.0")
It("ParseVersion3", func() {
var c PkgSelectorCondition = PkgCondLessEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("1.0"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser4", func() {
v, err := ParseVersion("<1.0")
It("ParseVersion4", func() {
var c PkgSelectorCondition = PkgCondLess
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("1.0"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser5", func() {
v, err := ParseVersion("=1.0")
It("ParseVersion5", func() {
var c PkgSelectorCondition = PkgCondEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("1.0"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser6", func() {
v, err := ParseVersion("!1.0")
It("ParseVersion6", func() {
var c PkgSelectorCondition = PkgCondNot
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("1.0"))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser7", func() {
v, err := ParseVersion("")
It("ParseVersion7", func() {
var c PkgSelectorCondition = PkgCondInvalid
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal(""))
Expect(v.VersionSuffix).Should(Equal(""))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser8", func() {
v, err := ParseVersion("=12.1.0.2_p1")
It("ParseVersion8", func() {
var c PkgSelectorCondition = PkgCondEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("12.1.0.2"))
Expect(v.VersionSuffix).Should(Equal("_p1"))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser9", func() {
v, err := ParseVersion(">=0.0.20190406.4.9.172-r1")
It("ParseVersion9", func() {
var c PkgSelectorCondition = PkgCondGreaterEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("0.0.20190406.4.9.172"))
Expect(v.VersionSuffix).Should(Equal("-r1"))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Versions Parser10", func() {
v, err := ParseVersion(">=0.0.20190406.4.9.172_alpha")
It("ParseVersion10", func() {
var c PkgSelectorCondition = PkgCondGreaterEqual
Expect(err).Should(BeNil())
Expect(v.Version).Should(Equal("0.0.20190406.4.9.172"))
Expect(v.VersionSuffix).Should(Equal("_alpha"))
Expect(v.Condition).Should(Equal(c))
})
})
Context("Selector1", func() {
v1, err := ParseVersion(">=0.0.20190406.4.9.172-r1")
v2, err2 := ParseVersion("1.0.111")
match, err3 := PackageAdmit(v1, v2)
It("Selector1", func() {
Expect(err).Should(BeNil())
Expect(err2).Should(BeNil())
Expect(err3).Should(BeNil())
Expect(match).Should(Equal(true))
})
})
Context("Selector2", func() {
v1, err := ParseVersion(">=0.0.20190406.4.9.172-r1")
v2, err2 := ParseVersion("0")
match, err3 := PackageAdmit(v1, v2)
It("Selector2", func() {
Expect(err).Should(BeNil())
Expect(err2).Should(BeNil())
Expect(err3).Should(BeNil())
Expect(match).Should(Equal(false))
})
})
Context("Selector3", func() {
v1, err := ParseVersion(">0")
v2, err2 := ParseVersion("0.0.40-alpha")
match, err3 := PackageAdmit(v1, v2)
It("Selector3", func() {
Expect(err).Should(BeNil())
Expect(err2).Should(BeNil())
Expect(err3).Should(BeNil())
Expect(match).Should(Equal(true))
})
})
Context("Selector4", func() {
v1, err := ParseVersion(">0")
v2, err2 := ParseVersion("")
match, err3 := PackageAdmit(v1, v2)
It("Selector4", func() {
Expect(err).Should(BeNil())
Expect(err2).Should(BeNil())
Expect(err3).Should(BeNil())
Expect(match).Should(Equal(true))
})
})
})