mirror of
https://github.com/mudler/luet.git
synced 2025-09-07 02:00:39 +00:00
Don't use semver library for ordering by default
Use debian to parse versions and selections. This covers ordering of versions like -0,-1 in sequence plus many others not available with semver. It is backward compatible as we do support the same featureset as before as tests are passing (adding more actually to cover interesting cases)
This commit is contained in:
@@ -26,7 +26,6 @@ import (
|
||||
|
||||
_gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
version "github.com/mudler/luet/pkg/versioner"
|
||||
)
|
||||
|
||||
func CreateRegexArray(rgx []string) ([]*regexp.Regexp, error) {
|
||||
@@ -56,63 +55,73 @@ func packageData(p string) (string, string) {
|
||||
}
|
||||
return cat, name
|
||||
}
|
||||
func ParsePackageStr(p string) (*pkg.DefaultPackage, error) {
|
||||
|
||||
if !(strings.HasPrefix(p, "=") || strings.HasPrefix(p, ">") ||
|
||||
strings.HasPrefix(p, "<")) {
|
||||
ver := ">=0"
|
||||
cat := ""
|
||||
name := ""
|
||||
func packageHasGentooSelector(v string) bool {
|
||||
return (strings.HasPrefix(v, "=") || strings.HasPrefix(v, ">") ||
|
||||
strings.HasPrefix(v, "<"))
|
||||
}
|
||||
|
||||
if strings.Contains(p, "@") {
|
||||
packageinfo := strings.Split(p, "@")
|
||||
ver = packageinfo[1]
|
||||
cat, name = packageData(packageinfo[0])
|
||||
} else {
|
||||
cat, name = packageData(p)
|
||||
}
|
||||
func gentooVersion(gp *_gentoo.GentooPackage) string {
|
||||
|
||||
return &pkg.DefaultPackage{
|
||||
Name: name,
|
||||
Category: cat,
|
||||
Version: ver,
|
||||
Uri: make([]string, 0),
|
||||
}, nil
|
||||
condition := gp.Condition.String()
|
||||
if condition == "=" {
|
||||
condition = ""
|
||||
}
|
||||
|
||||
gp, err := _gentoo.ParsePackageStr(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gp.Version == "" {
|
||||
gp.Version = "0"
|
||||
gp.Condition = _gentoo.PkgCondGreaterEqual
|
||||
}
|
||||
|
||||
pkgVersion := ""
|
||||
pkgVersion := fmt.Sprintf("%s%s%s",
|
||||
condition,
|
||||
gp.Version,
|
||||
gp.VersionSuffix,
|
||||
)
|
||||
if gp.VersionBuild != "" {
|
||||
pkgVersion = fmt.Sprintf("%s%s%s+%s",
|
||||
version.PkgSelectorConditionFromInt(gp.Condition.Int()).String(),
|
||||
condition,
|
||||
gp.Version,
|
||||
gp.VersionSuffix,
|
||||
gp.VersionBuild,
|
||||
)
|
||||
}
|
||||
return pkgVersion
|
||||
}
|
||||
|
||||
func ParsePackageStr(p string) (*pkg.DefaultPackage, error) {
|
||||
|
||||
if packageHasGentooSelector(p) {
|
||||
gp, err := _gentoo.ParsePackageStr(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gp.Version == "" {
|
||||
gp.Version = "0"
|
||||
gp.Condition = _gentoo.PkgCondGreaterEqual
|
||||
}
|
||||
|
||||
return &pkg.DefaultPackage{
|
||||
Name: gp.Name,
|
||||
Category: gp.Category,
|
||||
Version: gentooVersion(gp),
|
||||
Uri: make([]string, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
ver := ">=0"
|
||||
cat := ""
|
||||
name := ""
|
||||
|
||||
if strings.Contains(p, "@") {
|
||||
packageinfo := strings.Split(p, "@")
|
||||
ver = packageinfo[1]
|
||||
cat, name = packageData(packageinfo[0])
|
||||
} else {
|
||||
pkgVersion = fmt.Sprintf("%s%s%s",
|
||||
version.PkgSelectorConditionFromInt(gp.Condition.Int()).String(),
|
||||
gp.Version,
|
||||
gp.VersionSuffix,
|
||||
)
|
||||
cat, name = packageData(p)
|
||||
}
|
||||
|
||||
pack := &pkg.DefaultPackage{
|
||||
Name: gp.Name,
|
||||
Category: gp.Category,
|
||||
Version: pkgVersion,
|
||||
return &pkg.DefaultPackage{
|
||||
Name: name,
|
||||
Category: cat,
|
||||
Version: ver,
|
||||
Uri: make([]string, 0),
|
||||
}
|
||||
|
||||
return pack, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func CheckErr(err error) {
|
||||
|
@@ -1,336 +0,0 @@
|
||||
// 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 version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
semver "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 PkgSelectorConditionFromInt(c int) (ans PkgSelectorCondition) {
|
||||
if c == PkgCondGreater {
|
||||
ans = PkgCondGreater
|
||||
} else if c == PkgCondGreaterEqual {
|
||||
ans = PkgCondGreaterEqual
|
||||
} else if c == PkgCondLess {
|
||||
ans = PkgCondLess
|
||||
} else if c == PkgCondLessEqual {
|
||||
ans = PkgCondLessEqual
|
||||
} else if c == PkgCondNot {
|
||||
ans = PkgCondNot
|
||||
} else if c == PkgCondAnyRevision {
|
||||
ans = PkgCondAnyRevision
|
||||
} else if c == PkgCondMatchVersion {
|
||||
ans = PkgCondMatchVersion
|
||||
} else {
|
||||
ans = PkgCondInvalid
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p PkgSelectorCondition) String() (ans string) {
|
||||
if p == PkgCondInvalid {
|
||||
ans = ""
|
||||
} else if p == PkgCondGreater {
|
||||
ans = ">"
|
||||
} else if p == PkgCondGreaterEqual {
|
||||
ans = ">="
|
||||
} else if p == PkgCondLess {
|
||||
ans = "<"
|
||||
} else if p == PkgCondLessEqual {
|
||||
ans = "<="
|
||||
} else if p == PkgCondEqual {
|
||||
// To permit correct matching on database
|
||||
// we currently use directly package version without =
|
||||
ans = ""
|
||||
} else if p == PkgCondNot {
|
||||
ans = "!"
|
||||
} else if p == PkgCondAnyRevision {
|
||||
ans = "~"
|
||||
} else if p == PkgCondMatchVersion {
|
||||
ans = "=*"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p PkgSelectorCondition) Int() (ans int) {
|
||||
if p == PkgCondInvalid {
|
||||
ans = PkgCondInvalid
|
||||
} else if p == PkgCondGreater {
|
||||
ans = PkgCondGreater
|
||||
} else if p == PkgCondGreaterEqual {
|
||||
ans = PkgCondGreaterEqual
|
||||
} else if p == PkgCondLess {
|
||||
ans = PkgCondLess
|
||||
} else if p == PkgCondLessEqual {
|
||||
ans = PkgCondLessEqual
|
||||
} else if p == PkgCondEqual {
|
||||
// To permit correct matching on database
|
||||
// we currently use directly package version without =
|
||||
ans = PkgCondEqual
|
||||
} else if p == PkgCondNot {
|
||||
ans = PkgCondNot
|
||||
} else if p == PkgCondAnyRevision {
|
||||
ans = PkgCondAnyRevision
|
||||
} else if p == PkgCondMatchVersion {
|
||||
ans = PkgCondMatchVersion
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Check if build number is present
|
||||
buildIdx := strings.LastIndex(v, "+")
|
||||
buildVersion := ""
|
||||
if buildIdx > 0 {
|
||||
// <pre-release> ::= <dot-separated pre-release identifiers>
|
||||
//
|
||||
// <dot-separated pre-release identifiers> ::=
|
||||
// <pre-release identifier> | <pre-release identifier> "."
|
||||
// <dot-separated pre-release identifiers>
|
||||
//
|
||||
// <build> ::= <dot-separated build identifiers>
|
||||
//
|
||||
// <dot-separated build identifiers> ::= <build identifier>
|
||||
// | <build identifier> "." <dot-separated build identifiers>
|
||||
//
|
||||
// <pre-release identifier> ::= <alphanumeric identifier>
|
||||
// | <numeric identifier>
|
||||
//
|
||||
// <build identifier> ::= <alphanumeric identifier>
|
||||
// | <digits>
|
||||
//
|
||||
// <alphanumeric identifier> ::= <non-digit>
|
||||
// | <non-digit> <identifier characters>
|
||||
// | <identifier characters> <non-digit>
|
||||
// | <identifier characters> <non-digit> <identifier characters>
|
||||
buildVersion = v[buildIdx:]
|
||||
v = v[0:buildIdx]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
ans.Version += buildVersion
|
||||
|
||||
// NOTE: Now suffix complex like _alpha_rc1 are not supported.
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func PackageAdmit(selector, i PkgVersionSelector) (bool, error) {
|
||||
var v1 *semver.Version = nil
|
||||
var v2 *semver.Version = nil
|
||||
var ans bool
|
||||
var err error
|
||||
var sanitizedSelectorVersion, sanitizedIVersion string
|
||||
|
||||
if selector.Version != "" {
|
||||
// TODO: This is temporary!. I promise it.
|
||||
sanitizedSelectorVersion = strings.ReplaceAll(selector.Version, "_", "-")
|
||||
|
||||
v1, err = semver.NewVersion(sanitizedSelectorVersion)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if i.Version != "" {
|
||||
sanitizedIVersion = strings.ReplaceAll(i.Version, "_", "-")
|
||||
v2, err = semver.NewVersion(sanitizedIVersion)
|
||||
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(sanitizedIVersion, ".")
|
||||
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 := semver.NewConstraint(
|
||||
fmt.Sprintf(">= %s, < %s", sanitizedSelectorVersion, 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
|
||||
}
|
@@ -1,311 +0,0 @@
|
||||
// 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 version_test
|
||||
|
||||
import (
|
||||
gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo"
|
||||
|
||||
. "github.com/mudler/luet/pkg/versioner"
|
||||
. "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("Versions Parser11 - semver", func() {
|
||||
v, err := ParseVersion("0.1.0+0")
|
||||
It("ParseVersion10", func() {
|
||||
var c PkgSelectorCondition = PkgCondEqual
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(v.Version).Should(Equal("0.1.0+0"))
|
||||
Expect(v.VersionSuffix).Should(Equal(""))
|
||||
Expect(v.Condition).Should(Equal(c))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Versions Parser12 - semver", func() {
|
||||
v, err := ParseVersion(">=0.1.0_alpha+AB")
|
||||
It("ParseVersion10", func() {
|
||||
var c PkgSelectorCondition = PkgCondGreaterEqual
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(v.Version).Should(Equal("0.1.0+AB"))
|
||||
Expect(v.VersionSuffix).Should(Equal("_alpha"))
|
||||
Expect(v.Condition).Should(Equal(c))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Versions Parser13 - semver", func() {
|
||||
v, err := ParseVersion(">=0.1.0_alpha+0.1.22")
|
||||
It("ParseVersion10", func() {
|
||||
var c PkgSelectorCondition = PkgCondGreaterEqual
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(v.Version).Should(Equal("0.1.0+0.1.22"))
|
||||
Expect(v.VersionSuffix).Should(Equal("_alpha"))
|
||||
Expect(v.Condition).Should(Equal(c))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Versions Parser14 - semver", func() {
|
||||
v, err := ParseVersion(">=0.1.0_alpha+0.1.22")
|
||||
It("ParseVersion10", func() {
|
||||
var c PkgSelectorCondition = PkgCondGreaterEqual
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(v.Version).Should(Equal("0.1.0+0.1.22"))
|
||||
Expect(v.VersionSuffix).Should(Equal("_alpha"))
|
||||
Expect(v.Condition).Should(Equal(c))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Versions Parser15 - semver", func() {
|
||||
v, err := ParseVersion("<=0.3.222.4.5+AB")
|
||||
It("ParseVersion10", func() {
|
||||
var c PkgSelectorCondition = PkgCondLessEqual
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(v.Version).Should(Equal("0.3.222.4.5+AB"))
|
||||
Expect(v.VersionSuffix).Should(Equal(""))
|
||||
Expect(v.Condition).Should(Equal(c))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Versions Parser16 - semver", func() {
|
||||
v, err := ParseVersion("<=1.0.29+pre2_p20191024")
|
||||
It("ParseVersion10", func() {
|
||||
var c PkgSelectorCondition = PkgCondLessEqual
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(v.Version).Should(Equal("1.0.29+pre2_p20191024"))
|
||||
Expect(v.VersionSuffix).Should(Equal(""))
|
||||
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))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Selector5", func() {
|
||||
v1, err := ParseVersion(">0.1.0+0.4")
|
||||
v2, err2 := ParseVersion("0.1.0+0.3")
|
||||
match, err3 := PackageAdmit(v1, v2)
|
||||
It("Selector5", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(err2).Should(BeNil())
|
||||
Expect(err3).Should(BeNil())
|
||||
Expect(match).Should(Equal(false))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Selector6", func() {
|
||||
v1, err := ParseVersion(">=0.1.0+0.4")
|
||||
v2, err2 := ParseVersion("0.1.0+0.5")
|
||||
match, err3 := PackageAdmit(v1, v2)
|
||||
It("Selector6", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(err2).Should(BeNil())
|
||||
Expect(err3).Should(BeNil())
|
||||
Expect(match).Should(Equal(true))
|
||||
})
|
||||
})
|
||||
|
||||
PContext("Selector7", func() {
|
||||
v1, err := ParseVersion(">0.1.0+0.4")
|
||||
v2, err2 := ParseVersion("0.1.0+0.5")
|
||||
match, err3 := PackageAdmit(v1, v2)
|
||||
It("Selector7", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(err2).Should(BeNil())
|
||||
Expect(err3).Should(BeNil())
|
||||
Expect(match).Should(Equal(true))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Selector8", func() {
|
||||
v1, err := ParseVersion(">=0")
|
||||
v2, err2 := ParseVersion("1.0.29+pre2_p20191024")
|
||||
match, err3 := PackageAdmit(v1, v2)
|
||||
It("Selector8", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(err2).Should(BeNil())
|
||||
Expect(err3).Should(BeNil())
|
||||
Expect(match).Should(Equal(true))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Condition Converter 1", func() {
|
||||
gp, err := gentoo.ParsePackageStr("=layer/build-1.0")
|
||||
var cond gentoo.PackageCond = gentoo.PkgCondEqual
|
||||
It("Converter1", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect((*gp).Condition).Should(Equal(cond))
|
||||
Expect(PkgSelectorConditionFromInt((*gp).Condition.Int()).String()).Should(Equal(""))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
@@ -1,5 +1,4 @@
|
||||
// Copyright © 2019-2020 Ettore Di Giacinto <mudler@gentoo.org>,
|
||||
// Daniele Rondina <geaaru@sabayonlinux.org>
|
||||
// Copyright © 2019-2021 Ettore Di Giacinto <mudler@gentoo.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
|
||||
@@ -18,15 +17,79 @@ package version
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
semver "github.com/hashicorp/go-version"
|
||||
debversion "github.com/knqyf263/go-deb-version"
|
||||
)
|
||||
|
||||
const (
|
||||
selectorGreaterThen = iota
|
||||
selectorLessThen = iota
|
||||
selectorGreaterOrEqualThen = iota
|
||||
selectorLessOrEqualThen = iota
|
||||
selectorNotEqual = iota
|
||||
)
|
||||
|
||||
type packageSelector struct {
|
||||
Condition int
|
||||
Version string
|
||||
}
|
||||
|
||||
var selectors = map[string]int{
|
||||
">=": selectorGreaterOrEqualThen,
|
||||
">": selectorGreaterThen,
|
||||
"<=": selectorLessOrEqualThen,
|
||||
"<": selectorLessThen,
|
||||
"!": selectorNotEqual,
|
||||
}
|
||||
|
||||
func readPackageSelector(selector string) packageSelector {
|
||||
selectorType := 0
|
||||
v := ""
|
||||
|
||||
k := []string{}
|
||||
for kk, _ := range selectors {
|
||||
k = append(k, kk)
|
||||
}
|
||||
|
||||
sort.Slice(k, func(i, j int) bool {
|
||||
return len(k[i]) > len(k[j])
|
||||
})
|
||||
for _, p := range k {
|
||||
if strings.HasPrefix(selector, p) {
|
||||
selectorType = selectors[p]
|
||||
v = strings.TrimPrefix(selector, p)
|
||||
break
|
||||
}
|
||||
}
|
||||
return packageSelector{
|
||||
Condition: selectorType,
|
||||
Version: v,
|
||||
}
|
||||
}
|
||||
|
||||
func semverCheck(vv string, selector string) (bool, error) {
|
||||
c, err := semver.NewConstraint(selector)
|
||||
if err != nil {
|
||||
// Handle constraint not being parsable.
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion(vv)
|
||||
if err != nil {
|
||||
// Handle version not being parsable.
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if the version meets the constraints.
|
||||
return c.Check(v), nil
|
||||
}
|
||||
|
||||
// WrappedVersioner uses different means to return unique result that is understendable by Luet
|
||||
// It tries different approaches to sort, validate, and sanitize to a common versioning format
|
||||
// that is understendable by the whole code
|
||||
@@ -38,55 +101,49 @@ func DefaultVersioner() Versioner {
|
||||
|
||||
func (w *WrappedVersioner) Validate(version string) error {
|
||||
if !debversion.Valid(version) {
|
||||
return errors.New("Invalid version")
|
||||
return errors.New("invalid version")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WrappedVersioner) ValidateSelector(version string, selector string) bool {
|
||||
vS, err := ParseVersion(selector)
|
||||
func (w *WrappedVersioner) ValidateSelector(vv string, selector string) bool {
|
||||
if vv == "" {
|
||||
return true
|
||||
}
|
||||
vv = w.Sanitize(vv)
|
||||
selector = w.Sanitize(selector)
|
||||
|
||||
sel := readPackageSelector(selector)
|
||||
|
||||
selectorV, err := version.NewVersion(sel.Version)
|
||||
if err != nil {
|
||||
return false
|
||||
f, _ := semverCheck(vv, selector)
|
||||
return f
|
||||
}
|
||||
v, err := version.NewVersion(vv)
|
||||
if err != nil {
|
||||
f, _ := semverCheck(vv, selector)
|
||||
return f
|
||||
}
|
||||
|
||||
vSI, err := ParseVersion(version)
|
||||
if err != nil {
|
||||
return false
|
||||
switch sel.Condition {
|
||||
case selectorGreaterOrEqualThen:
|
||||
return v.GreaterThan(selectorV) || v.Equal(selectorV)
|
||||
case selectorLessOrEqualThen:
|
||||
return v.LessThan(selectorV) || v.Equal(selectorV)
|
||||
case selectorLessThen:
|
||||
return v.LessThan(selectorV)
|
||||
case selectorGreaterThen:
|
||||
return v.GreaterThan(selectorV)
|
||||
case selectorNotEqual:
|
||||
return !v.Equal(selectorV)
|
||||
}
|
||||
ok, err := PackageAdmit(vS, vSI)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return ok
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *WrappedVersioner) Sanitize(s string) string {
|
||||
return strings.ReplaceAll(s, "_", "-")
|
||||
}
|
||||
|
||||
func (w *WrappedVersioner) IsSemver(v string) bool {
|
||||
|
||||
// Taken https://github.com/hashicorp/go-version/blob/2b13044f5cdd3833370d41ce57d8bf3cec5e62b8/version.go#L44
|
||||
// semver doesn't have a Validate method, so we should filter before
|
||||
// going to use it blindly (it panics)
|
||||
semverRegexp := regexp.MustCompile("^" + semver.SemverRegexpRaw + "$")
|
||||
|
||||
// See https://github.com/hashicorp/go-version/blob/2b13044f5cdd3833370d41ce57d8bf3cec5e62b8/version.go#L61
|
||||
matches := semverRegexp.FindStringSubmatch(v)
|
||||
if matches == nil {
|
||||
return false
|
||||
}
|
||||
segmentsStr := strings.Split(matches[1], ".")
|
||||
segments := make([]int64, len(segmentsStr))
|
||||
for i, str := range segmentsStr {
|
||||
val, err := strconv.ParseInt(str, 10, 64)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
segments[i] = int64(val)
|
||||
}
|
||||
return (len(segments) != 0)
|
||||
return strings.TrimSpace(strings.ReplaceAll(s, "_", "-"))
|
||||
}
|
||||
|
||||
func (w *WrappedVersioner) Sort(toSort []string) []string {
|
||||
@@ -102,35 +159,6 @@ func (w *WrappedVersioner) Sort(toSort []string) []string {
|
||||
versionsRaw = append(versionsRaw, sanitizedVersion)
|
||||
}
|
||||
|
||||
versions := make([]*semver.Version, len(versionsRaw))
|
||||
|
||||
// Check if all of them are semver, otherwise we cannot do a good comparison
|
||||
allSemverCompliant := true
|
||||
for _, raw := range versionsRaw {
|
||||
if !w.IsSemver(raw) {
|
||||
allSemverCompliant = false
|
||||
}
|
||||
}
|
||||
|
||||
if allSemverCompliant {
|
||||
for i, raw := range versionsRaw {
|
||||
if w.IsSemver(raw) { // Make sure we include only semver, or go-version will panic
|
||||
v, _ := semver.NewVersion(raw)
|
||||
versions[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Try first semver sorting
|
||||
sort.Sort(semver.Collection(versions))
|
||||
if len(versions) > 0 {
|
||||
for _, v := range versions {
|
||||
result = append(result, versionsMap[v.Original()])
|
||||
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
// Try with debian sorting
|
||||
vs := make([]debversion.Version, len(versionsRaw))
|
||||
for i, r := range versionsRaw {
|
||||
v, _ := debversion.NewVersion(r)
|
||||
|
@@ -17,6 +17,8 @@
|
||||
package version_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "github.com/mudler/luet/pkg/versioner"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -71,6 +73,14 @@ var _ = Describe("Versioner", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Context("Sorting with +", func() {
|
||||
versioner := DefaultVersioner()
|
||||
It("finds the correct ordering", func() {
|
||||
sorted := versioner.Sort([]string{"1.0+1", "1.0+0", "0.1", "1.0+3", "1.0+2", "1.9"})
|
||||
Expect(sorted).Should(Equal([]string{"0.1", "1.0+0", "1.0+1", "1.0+2", "1.0+3", "1.9"}))
|
||||
})
|
||||
})
|
||||
|
||||
// from: https://github.com/knqyf263/go-deb-version/blob/master/version_test.go#L8
|
||||
Context("Debian Sorting", func() {
|
||||
versioner := DefaultVersioner()
|
||||
@@ -79,4 +89,75 @@ var _ = Describe("Versioner", func() {
|
||||
Expect(sorted).Should(Equal([]string{"2:7.4.052-1ubuntu1", "2:7.4.052-1ubuntu2", "2:7.4.052-1ubuntu3", "2:7.4.052-1ubuntu3.1"}))
|
||||
})
|
||||
})
|
||||
|
||||
It("finds the correct ordering", func() {
|
||||
versioner := DefaultVersioner()
|
||||
|
||||
sorted := versioner.Sort([]string{"0.0.1-beta-9", "0.0.1-alpha08-9", "0.0.1-alpha07-9", "0.0.1-alpha07-8"})
|
||||
Expect(sorted).Should(Equal([]string{"0.0.1-alpha07-8", "0.0.1-alpha07-9", "0.0.1-alpha08-9", "0.0.1-beta-9"}))
|
||||
})
|
||||
It("finds the correct ordering", func() {
|
||||
versioner := DefaultVersioner()
|
||||
|
||||
sorted := versioner.Sort([]string{"0.0.1-beta01", "0.0.1-alpha08", "0.0.1-alpha07"})
|
||||
Expect(sorted).Should(Equal([]string{"0.0.1-alpha07", "0.0.1-alpha08", "0.0.1-beta01"}))
|
||||
})
|
||||
|
||||
Context("Matching a selector", func() {
|
||||
|
||||
testCases := [][]string{
|
||||
{">=1", "2"},
|
||||
{"<=3", "2"},
|
||||
{">0", ""},
|
||||
{">0", "0.0.40-alpha"},
|
||||
{">=0.1.0+0.4", "0.1.0+0.5"},
|
||||
{">=0.0.20190406.4.9.172-r1", "1.0.111"},
|
||||
{">=0", "1.0.29+pre2_p20191024"},
|
||||
{">=0.1.0+4", "0.1.0+5"},
|
||||
{">0.1.0-4", "0.1.0-5"},
|
||||
{"<1.2.3-beta", "1.2.3-beta.1-1"},
|
||||
{"<1.2.3", "1.2.3-beta.1"},
|
||||
{">0.0.1-alpha07", "0.0.1-alpha07-8"},
|
||||
{">0.0.1-alpha07-1", "0.0.1-alpha07-8"},
|
||||
{">0.0.1-alpha07", "0.0.1-alpha08"},
|
||||
}
|
||||
versioner := DefaultVersioner()
|
||||
|
||||
for i, t := range testCases {
|
||||
selector := testCases[i][0]
|
||||
version := testCases[i][1]
|
||||
It(fmt.Sprint(t), func() {
|
||||
|
||||
Expect(versioner.ValidateSelector(version, selector)).Should(BeTrue())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Context("Not matching a selector", func() {
|
||||
|
||||
testfalseCases := [][]string{
|
||||
{">0.0.1-alpha07", "0.0.1-alpha06"},
|
||||
{"<0.0.1-alpha07", "0.0.1-alpha08"},
|
||||
{">0.1.0+0.4", "0.1.0+0.3"},
|
||||
{">=0.0.20190406.4.9.172-r1", "0"},
|
||||
{"<=1", "2"},
|
||||
{">=3", "2"},
|
||||
{"<0", "0.0.40-alpha"},
|
||||
{"<0.1.0+0.4", "0.1.0+0.5"},
|
||||
{"<=0.0.20190406.4.9.172-r1", "1.0.111"},
|
||||
{"<0.1.0+4", "0.1.0+5"},
|
||||
{"<=0.1.0-4", "0.1.0-5"},
|
||||
}
|
||||
|
||||
versioner := DefaultVersioner()
|
||||
|
||||
for i, t := range testfalseCases {
|
||||
selector := testfalseCases[i][0]
|
||||
version := testfalseCases[i][1]
|
||||
It(fmt.Sprint(t), func() {
|
||||
Expect(versioner.ValidateSelector(version, selector)).Should(BeFalse())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
Reference in New Issue
Block a user