diff --git a/cmd/install.go b/cmd/install.go index d54a63e8..92fe1358 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -46,7 +46,7 @@ var installCmd = &cobra.Command{ }, Long: `Install packages in parallel`, Run: func(cmd *cobra.Command, args []string) { - var toInstall []pkg.Package + var toInstall pkg.Packages var systemDB pkg.PackageDatabase for _, a := range args { diff --git a/cmd/root.go b/cmd/root.go index 30eb1cf5..24cf590e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -94,17 +94,19 @@ func LoadConfig(c *config.LuetConfig) error { func Execute() { if os.Getenv("LUET_NOLOCK") != "true" { - for _, lockedCmd := range LockedCommands { - if os.Args[1] == lockedCmd { - s := single.New("luet") - if err := s.CheckLock(); err != nil && err == single.ErrAlreadyRunning { - Fatal("another instance of the app is already running, exiting") - } else if err != nil { - // Another error occurred, might be worth handling it as well - Fatal("failed to acquire exclusive app lock:", err.Error()) + if len(os.Args) > 1 { + for _, lockedCmd := range LockedCommands { + if os.Args[1] == lockedCmd { + s := single.New("luet") + if err := s.CheckLock(); err != nil && err == single.ErrAlreadyRunning { + Fatal("another instance of the app is already running, exiting") + } else if err != nil { + // Another error occurred, might be worth handling it as well + Fatal("failed to acquire exclusive app lock:", err.Error()) + } + defer s.TryUnlock() + break } - defer s.TryUnlock() - break } } } diff --git a/cmd/search.go b/cmd/search.go index 6409c4d3..e54a5253 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -108,7 +108,7 @@ var searchCmd = &cobra.Command{ system := &installer.System{Database: systemDB, Target: LuetCfg.GetSystem().Rootfs} var err error - iMatches := []pkg.Package{} + iMatches := pkg.Packages{} if searchWithLabel { iMatches, err = system.Database.FindPackageLabel(args[0]) } else if searchWithLabelMatch { diff --git a/cmd/tree/bump.go b/cmd/tree/bump.go index 71eceeed..5c695f8d 100644 --- a/cmd/tree/bump.go +++ b/cmd/tree/bump.go @@ -41,6 +41,7 @@ func NewTreeBumpCommand() *cobra.Command { }, Run: func(cmd *cobra.Command, args []string) { spec, _ := cmd.Flags().GetString("definition-file") + toStdout, _ := cmd.Flags().GetBool("to-stdout") pack, err := tree.ReadDefinitionFile(spec) if err != nil { Fatal(err.Error()) @@ -52,16 +53,26 @@ func NewTreeBumpCommand() *cobra.Command { Fatal("Error on increment build version: " + err.Error()) } - err = tree.WriteDefinitionFile(&pack, spec) - if err != nil { - Fatal("Error on write definition file: " + err.Error()) - } + if toStdout { + data, err := pack.Yaml() + if err != nil { + Fatal("Error on yaml conversion: " + err.Error()) + } + fmt.Println(string(data)) + } else { - fmt.Printf("Bumped package %s/%s-%s.\n", pack.Category, pack.Name, pack.Version) + err = tree.WriteDefinitionFile(&pack, spec) + if err != nil { + Fatal("Error on write definition file: " + err.Error()) + } + + fmt.Printf("Bumped package %s/%s-%s.\n", pack.Category, pack.Name, pack.Version) + } }, } ans.Flags().StringP("definition-file", "f", "", "Path of the definition to bump.") + ans.Flags().BoolP("to-stdout", "o", false, "Bump package to output.") return ans } diff --git a/cmd/tree/validate.go b/cmd/tree/validate.go index 994d6770..3495786d 100644 --- a/cmd/tree/validate.go +++ b/cmd/tree/validate.go @@ -81,6 +81,34 @@ func NewTreeValidateCommand() *cobra.Command { for _, p := range reciper.GetDatabase().World() { + found, err := reciper.GetDatabase().FindPackages( + &pkg.DefaultPackage{ + Name: p.GetName(), + Category: p.GetCategory(), + Version: ">=0", + }, + ) + + if err != nil || len(found) < 1 { + if err != nil { + errstr = err.Error() + } else { + errstr = "No packages" + } + Error(fmt.Sprintf("%s/%s-%s: Broken. No versions could be found by database %s", + p.GetCategory(), p.GetName(), p.GetVersion(), + errstr, + )) + + errors = append(errors, + fmt.Sprintf("%s/%s-%s: Broken. No versions could be found by database %s", + p.GetCategory(), p.GetName(), p.GetVersion(), + errstr, + )) + + brokenPkgs++ + } + pkgstr := fmt.Sprintf("%s/%s-%s", p.GetCategory(), p.GetName(), p.GetVersion()) @@ -115,15 +143,22 @@ func NewTreeValidateCommand() *cobra.Command { } Info("Checking package "+fmt.Sprintf("%s/%s-%s", p.GetCategory(), p.GetName(), p.GetVersion()), "with", len(p.GetRequires()), "dependencies.") - for _, r := range p.GetRequires() { - - deps, err := reciper.GetDatabase().FindPackages( - &pkg.DefaultPackage{ - Name: r.GetName(), - Category: r.GetCategory(), - Version: r.GetVersion(), - }, - ) + all := p.GetRequires() + all = append(all, p.GetConflicts()...) + for _, r := range all { + var deps pkg.Packages + var err error + if r.IsSelector() { + deps, err = reciper.GetDatabase().FindPackages( + &pkg.DefaultPackage{ + Name: r.GetName(), + Category: r.GetCategory(), + Version: r.GetVersion(), + }, + ) + } else { + deps = append(deps, r) + } if err != nil || len(deps) < 1 { if err != nil { @@ -154,7 +189,11 @@ func NewTreeValidateCommand() *cobra.Command { if withSolver { Spinner(32) - _, err := depSolver.Install([]pkg.Package{r}) + solution, err := depSolver.Install(pkg.Packages{r}) + ass := solution.SearchByName(r.GetPackageName()) + if err == nil { + _, err = solution.Order(reciper.GetDatabase(), ass.Package.GetFingerPrint()) + } SpinnerStop() if err != nil { diff --git a/go.mod b/go.mod index d5ce30eb..ef780261 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,7 @@ go 1.12 require ( github.com/DataDog/zstd v1.4.4 // indirect - github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56 // indirect - github.com/Sabayon/pkgs-checker v0.6.2-0.20200315232328-b6efed54b4b1 + github.com/Sabayon/pkgs-checker v0.6.2-0.20200404093625-076438c31739 github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154 github.com/briandowns/spinner v1.7.0 github.com/cavaliercoder/grab v2.0.0+incompatible @@ -13,16 +12,15 @@ require ( github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23 github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd github.com/ghodss/yaml v1.0.0 - github.com/go-yaml/yaml v2.1.0+incompatible // indirect github.com/hashicorp/go-version v1.2.0 github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3 github.com/klauspost/pgzip v1.2.1 + github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d github.com/kr/pretty v0.2.0 // indirect github.com/kyokomi/emoji v2.1.0+incompatible github.com/logrusorgru/aurora v0.0.0-20190417123914-21d75270181e github.com/marcsauter/single v0.0.0-20181104081128-f8bf46f26ec0 github.com/mattn/go-isatty v0.0.10 // indirect - github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mudler/docker-companion v0.4.6-0.20191110154655-b8b364100616 github.com/onsi/ginkgo v1.10.1 github.com/onsi/gomega v1.7.0 diff --git a/go.sum b/go.sum index f0aed3ae..64ad9b91 100644 --- a/go.sum +++ b/go.sum @@ -9,24 +9,11 @@ github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6 github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56 h1:XCZM9J5KqLsr5NqtrZuXiD3X5fe5IfgU7IIUZzpeFBk= -github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56/go.mod h1:+Gbv6dg6TPHWq4oDjZY1vn978PLCEZ2hOu8kvn+S7t4= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7 h1:Vf80sSLu1ZWjjMmUKhw0FqM43lEOvT8O5B22NaHB6AQ= -github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= -github.com/Sabayon/pkgs-checker v0.5.0 h1:VRyyAxo6ox41Dytyl+K+QC+Tps0IxvqYbidu+AH+HUQ= -github.com/Sabayon/pkgs-checker v0.5.1-0.20200221202320-073693f2c657 h1:VK5S2Gh9kPUxX81zCFUgKVQn+hFy6VgyZMD3QLm76u8= -github.com/Sabayon/pkgs-checker v0.5.1-0.20200221202320-073693f2c657/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= -github.com/Sabayon/pkgs-checker v0.6.1 h1:7HIrrAQujfEQ0+vPjb1Px4AdUY7KWQPn8W0NjKMoGLI= -github.com/Sabayon/pkgs-checker v0.6.1/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= -github.com/Sabayon/pkgs-checker v0.6.2-0.20200310081024-935615ba9d27 h1:+5UCxj8bQHrcjuZumsX+E4mvol2F2dW8lXb1K+A/VlM= -github.com/Sabayon/pkgs-checker v0.6.2-0.20200310081024-935615ba9d27/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= -github.com/Sabayon/pkgs-checker v0.6.2-0.20200311072754-f4e0aec412f0 h1:C8pSo4uPvXcb5X2ai5oxuABdsOI1mlNNBaw2oxLbzy4= -github.com/Sabayon/pkgs-checker v0.6.2-0.20200311072754-f4e0aec412f0/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= -github.com/Sabayon/pkgs-checker v0.6.2-0.20200315232328-b6efed54b4b1 h1:0hkt4Na3ZDZO6e9hCKATOki+zBKOdRa0LxUtYlm9oyE= -github.com/Sabayon/pkgs-checker v0.6.2-0.20200315232328-b6efed54b4b1/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= +github.com/Sabayon/pkgs-checker v0.6.2-0.20200404093625-076438c31739 h1:cWiphrLut8a+RNwosFre5j+zWDe1m6y1OpkSzn5bu1w= +github.com/Sabayon/pkgs-checker v0.6.2-0.20200404093625-076438c31739/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= github.com/Sereal/Sereal v0.0.0-20181211220259-509a78ddbda3 h1:Xu7z47ZiE/J+sKXHZMGxEor/oY2q6dq51fkO0JqdSwY= github.com/Sereal/Sereal v0.0.0-20181211220259-509a78ddbda3/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -66,8 +53,6 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/crillab/gophersat v1.1.7 h1:f2Phe0W9jGyN1OefygKdcTdNM99q/goSjbWrFRjZGWc= -github.com/crillab/gophersat v1.1.7/go.mod h1:S91tHga1PCZzYhCkStwZAhvp1rCc+zqtSi55I+vDWGc= github.com/crillab/gophersat v1.1.9-0.20200211102949-9a8bf7f2f0a3 h1:HO63LCf9kTXQgUnlvFeS2qSDQhZ/cLP8DAJO89CythY= github.com/crillab/gophersat v1.1.9-0.20200211102949-9a8bf7f2f0a3/go.mod h1:S91tHga1PCZzYhCkStwZAhvp1rCc+zqtSi55I+vDWGc= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= @@ -104,8 +89,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= @@ -156,6 +139,8 @@ github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM= github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c= +github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -185,8 +170,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= diff --git a/pkg/compiler/backend/simpledocker.go b/pkg/compiler/backend/simpledocker.go index a8a720e3..6ba9bbc6 100644 --- a/pkg/compiler/backend/simpledocker.go +++ b/pkg/compiler/backend/simpledocker.go @@ -16,6 +16,7 @@ package backend import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -241,9 +242,17 @@ func (*SimpleDocker) Changes(fromImage, toImage string) ([]compiler.ArtifactLaye return []compiler.ArtifactLayer{}, errors.Wrap(err, "Error met while creating tempdir for rootfs") } defer os.RemoveAll(tmpdiffs) // clean up + var errorBuffer bytes.Buffer diffargs := []string{"diff", fromImage, toImage, "-v", "error", "-q", "--type=file", "-j", "-n", "-c", tmpdiffs} - out, err := exec.Command("container-diff", diffargs...).Output() + cmd := exec.Command("container-diff", diffargs...) + cmd.Stderr = &errorBuffer + out, err := cmd.Output() + + if string(errorBuffer.Bytes()) != "" { + Warning("container-diff errored with: " + string(errorBuffer.Bytes())) + } + if err != nil { return []compiler.ArtifactLayer{}, errors.Wrap(err, "Failed Resolving layer diffs: "+string(out)) } @@ -259,7 +268,7 @@ func (*SimpleDocker) Changes(fromImage, toImage string) ([]compiler.ArtifactLaye return []compiler.ArtifactLayer{}, errors.Wrap(err, "Failed unmarshalling json response: "+string(out)) } - if config.LuetCfg.GetLogging().Level == "debug" { + if config.LuetCfg.GetGeneral().Debug { summary := compiler.ComputeArtifactLayerSummary(diffs) for _, l := range summary.Layers { Debug(fmt.Sprintf("Diff %s -> %s: add %d (%d bytes), del %d (%d bytes), change %d (%d bytes)", diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 7de98bc6..1acbf4e3 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -433,12 +433,15 @@ func (cs *LuetCompiler) ComputeDepTree(p CompilationSpec) (solver.PackagesAssert s := solver.NewResolver(pkg.NewInMemoryDatabase(false), cs.Database, pkg.NewInMemoryDatabase(false), cs.Options.SolverOptions.Resolver()) - solution, err := s.Install([]pkg.Package{p.GetPackage()}) + solution, err := s.Install(pkg.Packages{p.GetPackage()}) if err != nil { return nil, errors.Wrap(err, "While computing a solution for "+p.GetPackage().HumanReadableString()) } - dependencies := solution.Order(cs.Database, p.GetPackage().GetFingerPrint()) + dependencies, err := solution.Order(cs.Database, p.GetPackage().GetFingerPrint()) + if err != nil { + return nil, errors.Wrap(err, "While order a solution for "+p.GetPackage().HumanReadableString()) + } assertions := solver.PackagesAssertions{} for _, assertion := range dependencies { //highly dependent on the order diff --git a/pkg/helpers/cli.go b/pkg/helpers/cli.go index 536a9c44..4c2f2ad8 100644 --- a/pkg/helpers/cli.go +++ b/pkg/helpers/cli.go @@ -23,6 +23,7 @@ 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) { @@ -53,14 +54,14 @@ func ParsePackageStr(p string) (*pkg.DefaultPackage, error) { pkgVersion := "" if gp.VersionBuild != "" { pkgVersion = fmt.Sprintf("%s%s%s+%s", - pkg.PkgSelectorConditionFromInt(gp.Condition.Int()).String(), + version.PkgSelectorConditionFromInt(gp.Condition.Int()).String(), gp.Version, gp.VersionSuffix, gp.VersionBuild, ) } else { pkgVersion = fmt.Sprintf("%s%s%s", - pkg.PkgSelectorConditionFromInt(gp.Condition.Int()).String(), + version.PkgSelectorConditionFromInt(gp.Condition.Int()).String(), gp.Version, gp.VersionSuffix, ) diff --git a/pkg/installer/installer.go b/pkg/installer/installer.go index 3f057696..b8dc0e4f 100644 --- a/pkg/installer/installer.go +++ b/pkg/installer/installer.go @@ -74,7 +74,7 @@ func (l *LuetInstaller) Upgrade(s *System) error { return errors.Wrap(err, "Failed solving solution for upgrade") } - toInstall := []pkg.Package{} + toInstall := pkg.Packages{} for _, assertion := range solution { // Be sure to filter from solutions packages already installed in the system if _, err := s.Database.FindPackage(assertion.Package); err != nil && assertion.Value { @@ -107,7 +107,7 @@ func (l *LuetInstaller) SyncRepositories(inMemory bool) (Repositories, error) { return syncedRepos, nil } -func (l *LuetInstaller) Swap(toRemove []pkg.Package, toInstall []pkg.Package, s *System) error { +func (l *LuetInstaller) Swap(toRemove pkg.Packages, toInstall pkg.Packages, s *System) error { syncedRepos, err := l.SyncRepositories(true) if err != nil { return err @@ -115,7 +115,7 @@ func (l *LuetInstaller) Swap(toRemove []pkg.Package, toInstall []pkg.Package, s return l.swap(syncedRepos, toRemove, toInstall, s) } -func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove []pkg.Package, toInstall []pkg.Package, s *System) error { +func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, toInstall pkg.Packages, s *System) error { // First match packages against repositories by priority allRepos := pkg.NewInMemoryDatabase(false) syncedRepos.SyncDatabase(allRepos) @@ -153,7 +153,7 @@ func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove []pkg.Package, t return l.install(syncedRepos, toInstall, s) } -func (l *LuetInstaller) Install(cp []pkg.Package, s *System, downloadOnly bool) error { +func (l *LuetInstaller) Install(cp pkg.Packages, s *System, downloadOnly bool) error { syncedRepos, err := l.SyncRepositories(true) if err != nil { return err @@ -161,7 +161,7 @@ func (l *LuetInstaller) Install(cp []pkg.Package, s *System, downloadOnly bool) return l.install(syncedRepos, cp, s) } -func (l *LuetInstaller) download(syncedRepos Repositories, cp []pkg.Package) error { +func (l *LuetInstaller) download(syncedRepos Repositories, cp pkg.Packages) error { toDownload := map[string]ArtifactMatch{} // FIXME: This can be optimized. We don't need to re-match this to the repository @@ -169,7 +169,7 @@ func (l *LuetInstaller) download(syncedRepos Repositories, cp []pkg.Package) err // Gathers things to download for _, currentPack := range cp { - matches := syncedRepos.PackageMatches([]pkg.Package{currentPack}) + matches := syncedRepos.PackageMatches(pkg.Packages{currentPack}) if len(matches) == 0 { return errors.New("Failed matching solutions against repository for " + currentPack.HumanReadableString() + " where are definitions coming from?!") } @@ -206,8 +206,8 @@ func (l *LuetInstaller) download(syncedRepos Repositories, cp []pkg.Package) err return nil } -func (l *LuetInstaller) install(syncedRepos Repositories, cp []pkg.Package, s *System) error { - var p []pkg.Package +func (l *LuetInstaller) install(syncedRepos Repositories, cp pkg.Packages, s *System) error { + var p pkg.Packages // Check if the package is installed first for _, pi := range cp { @@ -237,7 +237,7 @@ func (l *LuetInstaller) install(syncedRepos Repositories, cp []pkg.Package, s *S syncedRepos.SyncDatabase(allRepos) p = syncedRepos.ResolveSelectors(p) toInstall := map[string]ArtifactMatch{} - var packagesToInstall []pkg.Package + var packagesToInstall pkg.Packages var err error var solution solver.PackagesAssertions @@ -261,7 +261,7 @@ func (l *LuetInstaller) install(syncedRepos Repositories, cp []pkg.Package, s *S // Gathers things to install for _, currentPack := range packagesToInstall { - matches := syncedRepos.PackageMatches([]pkg.Package{currentPack}) + matches := syncedRepos.PackageMatches(pkg.Packages{currentPack}) if len(matches) == 0 { return errors.New("Failed matching solutions against repository for " + currentPack.HumanReadableString() + " where are definitions coming from?!") } @@ -326,7 +326,10 @@ func (l *LuetInstaller) install(syncedRepos Repositories, cp []pkg.Package, s *S // TODO: Lower those errors as warning for _, w := range p { // Finalizers needs to run in order and in sequence. - ordered := solution.Order(allRepos, w.GetFingerPrint()) + ordered, err := solution.Order(allRepos, w.GetFingerPrint()) + if err != nil { + return errors.Wrap(err, "While order a solution for "+w.HumanReadableString()) + } ORDER: for _, ass := range ordered { if ass.Value { diff --git a/pkg/installer/interface.go b/pkg/installer/interface.go index ac5119a1..64a64aa5 100644 --- a/pkg/installer/interface.go +++ b/pkg/installer/interface.go @@ -23,12 +23,12 @@ import ( ) type Installer interface { - Install([]pkg.Package, *System, bool) error + Install(pkg.Packages, *System, bool) error Uninstall(pkg.Package, *System) error Upgrade(s *System) error Repositories([]Repository) SyncRepositories(bool) (Repositories, error) - Swap([]pkg.Package, []pkg.Package, *System) error + Swap(pkg.Packages, pkg.Packages, *System) error } type Client interface { diff --git a/pkg/installer/repository.go b/pkg/installer/repository.go index 2fb1d5e5..ed85da4a 100644 --- a/pkg/installer/repository.go +++ b/pkg/installer/repository.go @@ -701,9 +701,9 @@ func (r Repositories) Less(i, j int) bool { return r[i].GetPriority() < r[j].GetPriority() } -func (r Repositories) World() []pkg.Package { +func (r Repositories) World() pkg.Packages { cache := map[string]pkg.Package{} - world := []pkg.Package{} + world := pkg.Packages{} // Get Uniques. Walk in reverse so the definitions of most prio-repo overwrites lower ones // In this way, when we will walk again later the deps sorting them by most higher prio we have better chance of success. @@ -741,7 +741,7 @@ type PackageMatch struct { Package pkg.Package } -func (re Repositories) PackageMatches(p []pkg.Package) []PackageMatch { +func (re Repositories) PackageMatches(p pkg.Packages) []PackageMatch { // TODO: Better heuristic. here we pick the first repo that contains the atom, sorted by priority but // we should do a permutations and get the best match, and in case there are more solutions the user should be able to pick sort.Sort(re) @@ -762,10 +762,10 @@ PACKAGE: } -func (re Repositories) ResolveSelectors(p []pkg.Package) []pkg.Package { +func (re Repositories) ResolveSelectors(p pkg.Packages) pkg.Packages { // If a selector is given, get the best from each repo sort.Sort(re) // respect prio - var matches []pkg.Package + var matches pkg.Packages PACKAGE: for _, pack := range p { REPOSITORY: @@ -798,7 +798,7 @@ func (re Repositories) SearchPackages(p string, o LuetSearchOpts) []PackageMatch var err error for _, r := range re { - var repoMatches []pkg.Package + var repoMatches pkg.Packages switch o.Mode { case SRegexPkg: diff --git a/pkg/installer/system.go b/pkg/installer/system.go index fc35ef67..e66d6eb4 100644 --- a/pkg/installer/system.go +++ b/pkg/installer/system.go @@ -9,6 +9,6 @@ type System struct { Target string } -func (s *System) World() ([]pkg.Package, error) { +func (s *System) World() (pkg.Packages, error) { return s.Database.World(), nil } diff --git a/pkg/package/database.go b/pkg/package/database.go index fe1f59c4..82983250 100644 --- a/pkg/package/database.go +++ b/pkg/package/database.go @@ -33,7 +33,7 @@ type PackageSet interface { GetPackage(ID string) (Package, error) Clean() error FindPackage(Package) (Package, error) - FindPackages(p Package) ([]Package, error) + FindPackages(p Package) (Packages, error) UpdatePackage(p Package) error GetAllPackages(packages chan Package) error RemovePackage(Package) error @@ -41,13 +41,13 @@ type PackageSet interface { GetPackageFiles(Package) ([]string, error) SetPackageFiles(*PackageFile) error RemovePackageFiles(Package) error - FindPackageVersions(p Package) ([]Package, error) - World() []Package + FindPackageVersions(p Package) (Packages, error) + World() Packages FindPackageCandidate(p Package) (Package, error) - FindPackageLabel(labelKey string) ([]Package, error) - FindPackageLabelMatch(pattern string) ([]Package, error) - FindPackageMatch(pattern string) ([]Package, error) + FindPackageLabel(labelKey string) (Packages, error) + FindPackageLabelMatch(pattern string) (Packages, error) + FindPackageMatch(pattern string) (Packages, error) } type PackageFile struct { diff --git a/pkg/package/database_boltdb.go b/pkg/package/database_boltdb.go index 50fbb528..63c4f0ed 100644 --- a/pkg/package/database_boltdb.go +++ b/pkg/package/database_boltdb.go @@ -233,7 +233,7 @@ func (db *BoltDatabase) getProvide(p Package) (Package, error) { for ve, _ := range versions { - match, err := p.VersionMatchSelector(ve) + match, err := p.VersionMatchSelector(ve, nil) if err != nil { return nil, errors.Wrap(err, "Error on match version") } @@ -314,7 +314,7 @@ func (db *BoltDatabase) RemovePackage(p Package) error { return nil } -func (db *BoltDatabase) World() []Package { +func (db *BoltDatabase) World() Packages { var all []Package // FIXME: This should all be locked in the db - for now forbid the solver to be run in threads. @@ -324,7 +324,7 @@ func (db *BoltDatabase) World() []Package { all = append(all, pack) } } - return all + return Packages(all) } func (db *BoltDatabase) FindPackageCandidate(p Package) (Package, error) { @@ -338,7 +338,7 @@ func (db *BoltDatabase) FindPackageCandidate(p Package) (Package, error) { if err != nil || len(packages) == 0 { required = p } else { - required = Best(packages) + required = packages.Best(nil) } return required, nil @@ -351,7 +351,7 @@ func (db *BoltDatabase) FindPackageCandidate(p Package) (Package, error) { // FindPackages return the list of the packages beloging to cat/name (any versions in requested range) // FIXME: Optimize, see inmemorydb -func (db *BoltDatabase) FindPackages(p Package) ([]Package, error) { +func (db *BoltDatabase) FindPackages(p Package) (Packages, error) { // Provides: Treat as the replaced package here if provided, err := db.getProvide(p); err == nil { p = provided @@ -362,7 +362,7 @@ func (db *BoltDatabase) FindPackages(p Package) ([]Package, error) { continue } - match, err := p.SelectorMatchVersion(w.GetVersion()) + match, err := p.SelectorMatchVersion(w.GetVersion(), nil) if err != nil { return nil, errors.Wrap(err, "Error on match selector") } @@ -370,11 +370,11 @@ func (db *BoltDatabase) FindPackages(p Package) ([]Package, error) { versionsInWorld = append(versionsInWorld, w) } } - return versionsInWorld, nil + return Packages(versionsInWorld), nil } // FindPackageVersions return the list of the packages beloging to cat/name -func (db *BoltDatabase) FindPackageVersions(p Package) ([]Package, error) { +func (db *BoltDatabase) FindPackageVersions(p Package) (Packages, error) { var versionsInWorld []Package for _, w := range db.World() { if w.GetName() != p.GetName() || w.GetCategory() != p.GetCategory() { @@ -383,10 +383,10 @@ func (db *BoltDatabase) FindPackageVersions(p Package) ([]Package, error) { versionsInWorld = append(versionsInWorld, w) } - return versionsInWorld, nil + return Packages(versionsInWorld), nil } -func (db *BoltDatabase) FindPackageLabel(labelKey string) ([]Package, error) { +func (db *BoltDatabase) FindPackageLabel(labelKey string) (Packages, error) { var ans []Package for _, k := range db.GetPackages() { @@ -398,10 +398,10 @@ func (db *BoltDatabase) FindPackageLabel(labelKey string) ([]Package, error) { ans = append(ans, pack) } } - return ans, nil + return Packages(ans), nil } -func (db *BoltDatabase) FindPackageLabelMatch(pattern string) ([]Package, error) { +func (db *BoltDatabase) FindPackageLabelMatch(pattern string) (Packages, error) { var ans []Package re := regexp.MustCompile(pattern) @@ -419,10 +419,10 @@ func (db *BoltDatabase) FindPackageLabelMatch(pattern string) ([]Package, error) } } - return ans, nil + return Packages(ans), nil } -func (db *BoltDatabase) FindPackageMatch(pattern string) ([]Package, error) { +func (db *BoltDatabase) FindPackageMatch(pattern string) (Packages, error) { var ans []Package re := regexp.MustCompile(pattern) @@ -441,5 +441,5 @@ func (db *BoltDatabase) FindPackageMatch(pattern string) ([]Package, error) { } } - return ans, nil + return Packages(ans), nil } diff --git a/pkg/package/database_mem.go b/pkg/package/database_mem.go index 6a8056be..1cf0d6ed 100644 --- a/pkg/package/database_mem.go +++ b/pkg/package/database_mem.go @@ -178,7 +178,7 @@ func (db *InMemoryDatabase) getProvide(p Package) (Package, error) { for ve, _ := range versions { - match, err := p.VersionMatchSelector(ve) + match, err := p.VersionMatchSelector(ve, nil) if err != nil { return nil, errors.Wrap(err, "Error on match version") } @@ -223,7 +223,7 @@ func (db *InMemoryDatabase) FindPackage(p Package) (Package, error) { } // FindPackages return the list of the packages beloging to cat/name -func (db *InMemoryDatabase) FindPackageVersions(p Package) ([]Package, error) { +func (db *InMemoryDatabase) FindPackageVersions(p Package) (Packages, error) { versions, ok := db.CacheNoVersion[p.GetPackageName()] if !ok { return nil, errors.New("No versions found for package") @@ -236,11 +236,11 @@ func (db *InMemoryDatabase) FindPackageVersions(p Package) ([]Package, error) { } versionsInWorld = append(versionsInWorld, w) } - return versionsInWorld, nil + return Packages(versionsInWorld), nil } // FindPackages return the list of the packages beloging to cat/name (any versions in requested range) -func (db *InMemoryDatabase) FindPackages(p Package) ([]Package, error) { +func (db *InMemoryDatabase) FindPackages(p Package) (Packages, error) { // Provides: Treat as the replaced package here if provided, err := db.getProvide(p); err == nil { @@ -252,7 +252,7 @@ func (db *InMemoryDatabase) FindPackages(p Package) ([]Package, error) { } var versionsInWorld []Package for ve, _ := range versions { - match, err := p.SelectorMatchVersion(ve) + match, err := p.SelectorMatchVersion(ve, nil) if err != nil { return nil, errors.Wrap(err, "Error on match selector") } @@ -265,7 +265,7 @@ func (db *InMemoryDatabase) FindPackages(p Package) ([]Package, error) { versionsInWorld = append(versionsInWorld, w) } } - return versionsInWorld, nil + return Packages(versionsInWorld), nil } func (db *InMemoryDatabase) UpdatePackage(p Package) error { @@ -327,7 +327,7 @@ func (db *InMemoryDatabase) RemovePackage(p Package) error { delete(db.Database, p.GetFingerPrint()) return nil } -func (db *InMemoryDatabase) World() []Package { +func (db *InMemoryDatabase) World() Packages { var all []Package // FIXME: This should all be locked in the db - for now forbid the solver to be run in threads. for _, k := range db.GetPackages() { @@ -336,7 +336,7 @@ func (db *InMemoryDatabase) World() []Package { all = append(all, pack) } } - return all + return Packages(all) } func (db *InMemoryDatabase) FindPackageCandidate(p Package) (Package, error) { @@ -349,7 +349,7 @@ func (db *InMemoryDatabase) FindPackageCandidate(p Package) (Package, error) { if err != nil || len(packages) == 0 { required = p } else { - required = Best(packages) + required = packages.Best(nil) } return required, nil @@ -360,7 +360,7 @@ func (db *InMemoryDatabase) FindPackageCandidate(p Package) (Package, error) { } -func (db *InMemoryDatabase) FindPackageLabel(labelKey string) ([]Package, error) { +func (db *InMemoryDatabase) FindPackageLabel(labelKey string) (Packages, error) { var ans []Package for _, k := range db.GetPackages() { @@ -373,10 +373,10 @@ func (db *InMemoryDatabase) FindPackageLabel(labelKey string) ([]Package, error) } } - return ans, nil + return Packages(ans), nil } -func (db *InMemoryDatabase) FindPackageLabelMatch(pattern string) ([]Package, error) { +func (db *InMemoryDatabase) FindPackageLabelMatch(pattern string) (Packages, error) { var ans []Package re := regexp.MustCompile(pattern) @@ -394,10 +394,10 @@ func (db *InMemoryDatabase) FindPackageLabelMatch(pattern string) ([]Package, er } } - return ans, nil + return Packages(ans), nil } -func (db *InMemoryDatabase) FindPackageMatch(pattern string) ([]Package, error) { +func (db *InMemoryDatabase) FindPackageMatch(pattern string) (Packages, error) { var ans []Package re := regexp.MustCompile(pattern) @@ -416,5 +416,5 @@ func (db *InMemoryDatabase) FindPackageMatch(pattern string) ([]Package, error) } } - return ans, nil + return Packages(ans), nil } diff --git a/pkg/package/package.go b/pkg/package/package.go index fbe68701..adb07f1c 100644 --- a/pkg/package/package.go +++ b/pkg/package/package.go @@ -23,15 +23,14 @@ import ( "io" "path/filepath" "regexp" - "sort" "strconv" "strings" gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo" "github.com/crillab/gophersat/bf" "github.com/ghodss/yaml" - version "github.com/hashicorp/go-version" "github.com/jinzhu/copier" + version "github.com/mudler/luet/pkg/versioner" "github.com/pkg/errors" ) @@ -47,15 +46,15 @@ type Package interface { GetPackageName() string Requires([]*DefaultPackage) Package Conflicts([]*DefaultPackage) Package - Revdeps(PackageDatabase) []Package - LabelDeps(PackageDatabase, string) []Package + Revdeps(PackageDatabase) Packages + LabelDeps(PackageDatabase, string) Packages GetProvides() []*DefaultPackage SetProvides([]*DefaultPackage) Package GetRequires() []*DefaultPackage GetConflicts() []*DefaultPackage - Expand(PackageDatabase) ([]Package, error) + Expand(PackageDatabase) (Packages, error) SetCategory(string) GetName() string @@ -92,8 +91,8 @@ type Package interface { MatchLabel(*regexp.Regexp) bool IsSelector() bool - VersionMatchSelector(string) (bool, error) - SelectorMatchVersion(string) (bool, error) + VersionMatchSelector(string, version.Versioner) (bool, error) + SelectorMatchVersion(string, version.Versioner) (bool, error) String() string HumanReadableString() string @@ -104,10 +103,12 @@ type Tree interface { GetPackageSet() PackageDatabase Prelude() string // A tree might have a prelude to be able to consume a tree SetPackageSet(s PackageDatabase) - World() ([]Package, error) + World() (Packages, error) FindPackage(Package) (Package, error) } +type Packages []Package + // >> Unmarshallers // DefaultPackageFromYaml decodes a package from yaml bytes func DefaultPackageFromYaml(yml []byte) (DefaultPackage, error) { @@ -378,15 +379,15 @@ func (p *DefaultPackage) Matches(m Package) bool { return false } -func (p *DefaultPackage) Expand(definitiondb PackageDatabase) ([]Package, error) { - var versionsInWorld []Package +func (p *DefaultPackage) Expand(definitiondb PackageDatabase) (Packages, error) { + var versionsInWorld Packages all, err := definitiondb.FindPackages(p) if err != nil { return nil, err } for _, w := range all { - match, err := p.SelectorMatchVersion(w.GetVersion()) + match, err := p.SelectorMatchVersion(w.GetVersion(), nil) if err != nil { return nil, err } @@ -398,8 +399,8 @@ func (p *DefaultPackage) Expand(definitiondb PackageDatabase) ([]Package, error) return versionsInWorld, nil } -func (p *DefaultPackage) Revdeps(definitiondb PackageDatabase) []Package { - var versionsInWorld []Package +func (p *DefaultPackage) Revdeps(definitiondb PackageDatabase) Packages { + var versionsInWorld Packages for _, w := range definitiondb.World() { if w.Matches(p) { continue @@ -415,8 +416,8 @@ func (p *DefaultPackage) Revdeps(definitiondb PackageDatabase) []Package { return versionsInWorld } -func (p *DefaultPackage) LabelDeps(definitiondb PackageDatabase, labelKey string) []Package { - var pkgsWithLabelInWorld []Package +func (p *DefaultPackage) LabelDeps(definitiondb PackageDatabase, labelKey string) Packages { + var pkgsWithLabelInWorld Packages // TODO: check if integrate some index to improve // research instead of iterate all list. for _, w := range definitiondb.World() { @@ -458,7 +459,13 @@ func (pack *DefaultPackage) RequiresContains(definitiondb PackageDatabase, s Pac return false, nil } -func Best(set []Package) Package { +// Best returns the best version of the package (the most bigger) from a list +// Accepts a versioner interface to change the ordering policy. If null is supplied +// It defaults to version.WrappedVersioner which supports both semver and debian versioning +func (set Packages) Best(v version.Versioner) Package { + if v == nil { + v = &version.WrappedVersioner{} + } var versionsMap map[string]Package = make(map[string]Package) if len(set) == 0 { panic("Best needs a list with elements") @@ -469,17 +476,9 @@ func Best(set []Package) Package { versionsRaw = append(versionsRaw, p.GetVersion()) versionsMap[p.GetVersion()] = p } + sorted := v.Sort(versionsRaw) - versions := make([]*version.Version, len(versionsRaw)) - for i, raw := range versionsRaw { - v, _ := version.NewVersion(raw) - versions[i] = v - } - - // After this, the versions are properly sorted - sort.Sort(version.Collection(versions)) - - return versionsMap[versions[len(versions)-1].Original()] + return versionsMap[sorted[len(sorted)-1]] } func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db PackageDatabase) ([]bf.Formula, error) { @@ -750,3 +749,22 @@ end: return nil } + +func (p *DefaultPackage) SelectorMatchVersion(ver string, v version.Versioner) (bool, error) { + if !p.IsSelector() { + return false, errors.New("Package is not a selector") + } + if v == nil { + v = &version.WrappedVersioner{} + } + + return v.ValidateSelector(ver, p.GetVersion()), nil +} + +func (p *DefaultPackage) VersionMatchSelector(selector string, v version.Versioner) (bool, error) { + if v == nil { + v = &version.WrappedVersioner{} + } + + return v.ValidateSelector(p.GetVersion(), selector), nil +} diff --git a/pkg/package/package_test.go b/pkg/package/package_test.go index b007a34c..31a42032 100644 --- a/pkg/package/package_test.go +++ b/pkg/package/package_test.go @@ -58,7 +58,7 @@ var _ = Describe("Package", func() { Expect(lst).To(ContainElement(a1)) Expect(lst).ToNot(ContainElement(a01)) Expect(len(lst)).To(Equal(2)) - p := Best(lst) + p := lst.Best(nil) Expect(p).To(Equal(a11)) }) }) diff --git a/pkg/solver/decoder.go b/pkg/solver/decoder.go index 86bbc19f..bbdf0ae9 100644 --- a/pkg/solver/decoder.go +++ b/pkg/solver/decoder.go @@ -23,6 +23,7 @@ import ( pkg "github.com/mudler/luet/pkg/package" toposort "github.com/philopon/go-toposort" + "github.com/pkg/errors" "github.com/stevenle/topsort" ) @@ -145,7 +146,7 @@ func (assertions PackagesAssertions) Search(f string) *PackageAssert { return nil } -func (assertions PackagesAssertions) Order(definitiondb pkg.PackageDatabase, fingerprint string) PackagesAssertions { +func (assertions PackagesAssertions) Order(definitiondb pkg.PackageDatabase, fingerprint string) (PackagesAssertions, error) { orderedAssertions := PackagesAssertions{} unorderedAssertions := PackagesAssertions{} @@ -191,19 +192,19 @@ func (assertions PackagesAssertions) Order(definitiondb pkg.PackageDatabase, fin } result, err := graph.TopSort(fingerprint) if err != nil { - panic(err) + return nil, errors.Wrap(err, "fail on sorting "+fingerprint) } for _, res := range result { a, ok := tmpMap[res] if !ok { - panic("fail looking for " + res) + return nil, errors.New("fail looking for " + res) // continue } orderedAssertions = append(orderedAssertions, a) // orderedAssertions = append(PackagesAssertions{a}, orderedAssertions...) // push upfront } //helpers.ReverseAny(orderedAssertions) - return orderedAssertions + return orderedAssertions, nil } func (assertions PackagesAssertions) Explain() string { diff --git a/pkg/solver/decoder_test.go b/pkg/solver/decoder_test.go index d52d5227..c888b6d7 100644 --- a/pkg/solver/decoder_test.go +++ b/pkg/solver/decoder_test.go @@ -72,7 +72,8 @@ var _ = Describe("Decoder", func() { Expect(len(solution)).To(Equal(6)) Expect(err).ToNot(HaveOccurred()) - solution = solution.Order(dbDefinitions, A.GetFingerPrint()) + solution, err = solution.Order(dbDefinitions, A.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) // Expect(len(solution)).To(Equal(6)) Expect(solution[0].Package.GetName()).To(Equal("G")) Expect(solution[1].Package.GetName()).To(Equal("H")) @@ -188,7 +189,8 @@ var _ = Describe("Decoder", func() { Expect(len(solution)).To(Equal(6)) Expect(err).ToNot(HaveOccurred()) - solution = solution.Order(dbDefinitions, A.GetFingerPrint()) + solution, err = solution.Order(dbDefinitions, A.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) // Expect(len(solution)).To(Equal(6)) Expect(solution[0].Package.GetName()).To(Equal("G")) Expect(solution[1].Package.GetName()).To(Equal("H")) @@ -206,7 +208,8 @@ var _ = Describe("Decoder", func() { Expect(len(solution)).To(Equal(6)) Expect(err).ToNot(HaveOccurred()) - solution = solution.Order(dbDefinitions, B.GetFingerPrint()) + solution, err = solution.Order(dbDefinitions, B.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) hash2 := solution.AssertionHash() // Expect(len(solution)).To(Equal(6)) @@ -243,7 +246,11 @@ var _ = Describe("Decoder", func() { solution2, err := s.Install([]pkg.Package{Z}) Expect(err).ToNot(HaveOccurred()) - Expect(solution.Order(dbDefinitions, Y.GetFingerPrint()).Drop(Y).AssertionHash() == solution2.Order(dbDefinitions, Z.GetFingerPrint()).Drop(Z).AssertionHash()).To(BeTrue()) + orderY, err := solution.Order(dbDefinitions, Y.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) + orderZ, err := solution2.Order(dbDefinitions, Z.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) + Expect(orderY.Drop(Y).AssertionHash() == orderZ.Drop(Z).AssertionHash()).To(BeTrue()) }) It("Hashes them, Cuts them and could be used for comparison", func() { @@ -267,9 +274,13 @@ var _ = Describe("Decoder", func() { solution2, err := s.Install([]pkg.Package{Z}) Expect(err).ToNot(HaveOccurred()) - Expect(solution.Order(dbDefinitions, Y.GetFingerPrint()).Cut(Y).Drop(Y)).To(Equal(solution2.Order(dbDefinitions, Z.GetFingerPrint()).Cut(Z).Drop(Z))) + orderY, err := solution.Order(dbDefinitions, Y.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) + orderZ, err := solution2.Order(dbDefinitions, Z.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) + Expect(orderY.Cut(Y).Drop(Y)).To(Equal(orderZ.Cut(Z).Drop(Z))) - Expect(solution.Order(dbDefinitions, Y.GetFingerPrint()).Cut(Y).Drop(Y).AssertionHash()).To(Equal(solution2.Order(dbDefinitions, Z.GetFingerPrint()).Cut(Z).Drop(Z).AssertionHash())) + Expect(orderY.Cut(Y).Drop(Y).AssertionHash()).To(Equal(orderZ.Cut(Z).Drop(Z).AssertionHash())) }) }) diff --git a/pkg/solver/resolver.go b/pkg/solver/resolver.go index 21f9e783..941da67b 100644 --- a/pkg/solver/resolver.go +++ b/pkg/solver/resolver.go @@ -77,11 +77,11 @@ type QLearningResolver struct { Solver PackageSolver Formula bf.Formula - Targets []pkg.Package - Current []pkg.Package + Targets pkg.Packages + Current pkg.Packages observedDelta int - observedDeltaChoice []pkg.Package + observedDeltaChoice pkg.Packages Agent *qlearning.SimpleAgent } @@ -177,7 +177,7 @@ func (resolver *QLearningResolver) Try(c Choice) error { packtoAdd := pkg.FromString(pack) resolver.Attempted[pack+strconv.Itoa(int(c.Action))] = true // increase the count s, _ := resolver.Solver.(*Solver) - var filtered []pkg.Package + var filtered pkg.Packages switch c.Action { case ActionAdded: diff --git a/pkg/solver/solver.go b/pkg/solver/solver.go index f6012fae..5befe464 100644 --- a/pkg/solver/solver.go +++ b/pkg/solver/solver.go @@ -27,12 +27,12 @@ import ( // PackageSolver is an interface to a generic package solving algorithm type PackageSolver interface { SetDefinitionDatabase(pkg.PackageDatabase) - Install(p []pkg.Package) (PackagesAssertions, error) - Uninstall(candidate pkg.Package, checkconflicts bool) ([]pkg.Package, error) + Install(p pkg.Packages) (PackagesAssertions, error) + Uninstall(candidate pkg.Package, checkconflicts bool) (pkg.Packages, error) ConflictsWithInstalled(p pkg.Package) (bool, error) - ConflictsWith(p pkg.Package, ls []pkg.Package) (bool, error) - World() []pkg.Package - Upgrade(checkconflicts bool) ([]pkg.Package, PackagesAssertions, error) + ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error) + World() pkg.Packages + Upgrade(checkconflicts bool) (pkg.Packages, PackagesAssertions, error) SetResolver(PackageResolver) @@ -43,7 +43,7 @@ type PackageSolver interface { type Solver struct { DefinitionDatabase pkg.PackageDatabase SolverDatabase pkg.PackageDatabase - Wanted []pkg.Package + Wanted pkg.Packages InstalledDatabase pkg.PackageDatabase Resolver PackageResolver @@ -72,11 +72,11 @@ func (s *Solver) SetResolver(r PackageResolver) { s.Resolver = r } -func (s *Solver) World() []pkg.Package { +func (s *Solver) World() pkg.Packages { return s.DefinitionDatabase.World() } -func (s *Solver) Installed() []pkg.Package { +func (s *Solver) Installed() pkg.Packages { return s.InstalledDatabase.World() } @@ -130,8 +130,8 @@ func (s *Solver) BuildWorld(includeInstalled bool) (bf.Formula, error) { return bf.And(formulas...), nil } -func (s *Solver) getList(db pkg.PackageDatabase, lsp []pkg.Package) ([]pkg.Package, error) { - var ls []pkg.Package +func (s *Solver) getList(db pkg.PackageDatabase, lsp pkg.Packages) (pkg.Packages, error) { + var ls pkg.Packages for _, pp := range lsp { cp, err := db.FindPackage(pp) @@ -141,7 +141,7 @@ func (s *Solver) getList(db pkg.PackageDatabase, lsp []pkg.Package) ([]pkg.Packa if err != nil || len(packages) == 0 { cp = pp } else { - cp = pkg.Best(packages) + cp = packages.Best(nil) } } ls = append(ls, cp) @@ -149,7 +149,7 @@ func (s *Solver) getList(db pkg.PackageDatabase, lsp []pkg.Package) ([]pkg.Packa return ls, nil } -func (s *Solver) ConflictsWith(pack pkg.Package, lsp []pkg.Package) (bool, error) { +func (s *Solver) ConflictsWith(pack pkg.Package, lsp pkg.Packages) (bool, error) { p, err := s.DefinitionDatabase.FindPackage(pack) if err != nil { p = pack //Relax search, otherwise we cannot compute solutions for packages not in definitions @@ -210,14 +210,14 @@ func (s *Solver) ConflictsWithInstalled(p pkg.Package) (bool, error) { return s.ConflictsWith(p, s.Installed()) } -func (s *Solver) Upgrade(checkconflicts bool) ([]pkg.Package, PackagesAssertions, error) { +func (s *Solver) Upgrade(checkconflicts bool) (pkg.Packages, PackagesAssertions, error) { // First get candidates that needs to be upgraded.. - toUninstall := []pkg.Package{} - toInstall := []pkg.Package{} + toUninstall := pkg.Packages{} + toInstall := pkg.Packages{} - availableCache := map[string][]pkg.Package{} + availableCache := map[string]pkg.Packages{} for _, p := range s.DefinitionDatabase.World() { // Each one, should be expanded availableCache[p.GetName()+p.GetCategory()] = append(availableCache[p.GetName()+p.GetCategory()], p) @@ -229,7 +229,7 @@ func (s *Solver) Upgrade(checkconflicts bool) ([]pkg.Package, PackagesAssertions installedcopy.CreatePackage(p) packages, ok := availableCache[p.GetName()+p.GetCategory()] if ok && len(packages) != 0 { - best := pkg.Best(packages) + best := packages.Best(nil) if best.GetVersion() != p.GetVersion() { toUninstall = append(toUninstall, p) toInstall = append(toInstall, best) @@ -261,8 +261,8 @@ func (s *Solver) Upgrade(checkconflicts bool) ([]pkg.Package, PackagesAssertions // Uninstall takes a candidate package and return a list of packages that would be removed // in order to purge the candidate. Returns error if unsat. -func (s *Solver) Uninstall(c pkg.Package, checkconflicts bool) ([]pkg.Package, error) { - var res []pkg.Package +func (s *Solver) Uninstall(c pkg.Package, checkconflicts bool) (pkg.Packages, error) { + var res pkg.Packages candidate, err := s.InstalledDatabase.FindPackage(c) if err != nil { @@ -272,13 +272,13 @@ func (s *Solver) Uninstall(c pkg.Package, checkconflicts bool) ([]pkg.Package, e if err != nil || len(packages) == 0 { candidate = c } else { - candidate = pkg.Best(packages) + candidate = packages.Best(nil) } //Relax search, otherwise we cannot compute solutions for packages not in definitions // return nil, errors.Wrap(err, "Package not found between installed") } // Build a fake "Installed" - Candidate and its requires tree - var InstalledMinusCandidate []pkg.Package + var InstalledMinusCandidate pkg.Packages // TODO: Can be optimized for _, i := range s.Installed() { @@ -296,7 +296,7 @@ func (s *Solver) Uninstall(c pkg.Package, checkconflicts bool) ([]pkg.Package, e s2 := NewSolver(pkg.NewInMemoryDatabase(false), s.DefinitionDatabase, pkg.NewInMemoryDatabase(false)) s2.SetResolver(s.Resolver) // Get the requirements to install the candidate - asserts, err := s2.Install([]pkg.Package{candidate}) + asserts, err := s2.Install(pkg.Packages{candidate}) if err != nil { return nil, err } @@ -403,7 +403,7 @@ func (s *Solver) Solve() (PackagesAssertions, error) { // Install given a list of packages, returns package assertions to indicate the packages that must be installed in the system in order // to statisfy all the constraints -func (s *Solver) Install(c []pkg.Package) (PackagesAssertions, error) { +func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) { coll, err := s.getList(s.DefinitionDatabase, c) if err != nil { diff --git a/pkg/solver/solver_test.go b/pkg/solver/solver_test.go index 56482743..af507c6e 100644 --- a/pkg/solver/solver_test.go +++ b/pkg/solver/solver_test.go @@ -882,7 +882,7 @@ var _ = Describe("Solver", func() { Expect(lst).To(ContainElement(a03)) Expect(lst).ToNot(ContainElement(old)) Expect(len(lst)).To(Equal(5)) - p := pkg.Best(lst) + p := lst.Best(nil) Expect(p).To(Equal(a03)) }) }) diff --git a/pkg/tree/builder/gentoo/gentoo.go b/pkg/tree/builder/gentoo/gentoo.go index 0831bcc7..e520f019 100644 --- a/pkg/tree/builder/gentoo/gentoo.go +++ b/pkg/tree/builder/gentoo/gentoo.go @@ -50,7 +50,7 @@ type GentooBuilder struct { } type EbuildParser interface { - ScanEbuild(string) ([]pkg.Package, error) + ScanEbuild(string) (pkg.Packages, error) } func (gb *GentooBuilder) scanEbuild(path string, db pkg.PackageDatabase) error { diff --git a/pkg/tree/builder/gentoo/gentoo_test.go b/pkg/tree/builder/gentoo/gentoo_test.go index f1772d9c..3d0e2d4e 100644 --- a/pkg/tree/builder/gentoo/gentoo_test.go +++ b/pkg/tree/builder/gentoo/gentoo_test.go @@ -27,8 +27,8 @@ import ( type FakeParser struct { } -func (f *FakeParser) ScanEbuild(path string) ([]pkg.Package, error) { - return []pkg.Package{&pkg.DefaultPackage{Name: path}}, nil +func (f *FakeParser) ScanEbuild(path string) (pkg.Packages, error) { + return pkg.Packages{&pkg.DefaultPackage{Name: path}}, nil } var _ = Describe("GentooBuilder", func() { diff --git a/pkg/tree/builder/gentoo/simpleparser.go b/pkg/tree/builder/gentoo/simpleparser.go index 98ee6efa..41625a46 100644 --- a/pkg/tree/builder/gentoo/simpleparser.go +++ b/pkg/tree/builder/gentoo/simpleparser.go @@ -323,7 +323,7 @@ func SourceFile(ctx context.Context, path string, pkg *_gentoo.GentooPackage) (m } // ScanEbuild returns a list of packages (always one with SimpleEbuildParser) decoded from an ebuild. -func (ep *SimpleEbuildParser) ScanEbuild(path string) ([]pkg.Package, error) { +func (ep *SimpleEbuildParser) ScanEbuild(path string) (pkg.Packages, error) { Debug("Starting parsing of ebuild", path) pkgstr := filepath.Base(path) @@ -332,7 +332,7 @@ func (ep *SimpleEbuildParser) ScanEbuild(path string) ([]pkg.Package, error) { gp, err := _gentoo.ParsePackageStr(pkgstr) if err != nil { - return []pkg.Package{}, errors.New("Error on parsing package string") + return pkg.Packages{}, errors.New("Error on parsing package string") } pack := &pkg.DefaultPackage{ @@ -350,7 +350,7 @@ func (ep *SimpleEbuildParser) ScanEbuild(path string) ([]pkg.Package, error) { vars, err := SourceFile(timeout, path, gp) if err != nil { Error("Error on source file ", pack.Name, ": ", err) - return []pkg.Package{}, err + return pkg.Packages{}, err } // TODO: Handle this a bit better @@ -405,8 +405,8 @@ func (ep *SimpleEbuildParser) ScanEbuild(path string) ([]pkg.Package, error) { gRDEPEND, err := ParseRDEPEND(rdepend.String()) if err != nil { Warning("Error on parsing RDEPEND for package ", pack.Category+"/"+pack.Name, err) - return []pkg.Package{pack}, nil - // return []pkg.Package{}, err + return pkg.Packages{pack}, nil + // return pkg.Packages{}, err } pack.PackageConflicts = []*pkg.DefaultPackage{} @@ -436,5 +436,5 @@ func (ep *SimpleEbuildParser) ScanEbuild(path string) ([]pkg.Package, error) { Debug("Finished processing ebuild", path, "deps ", len(pack.PackageRequires)) //TODO: Deps and conflicts - return []pkg.Package{pack}, nil + return pkg.Packages{pack}, nil } diff --git a/pkg/tree/tree_test.go b/pkg/tree/tree_test.go index e76706c7..2550e297 100644 --- a/pkg/tree/tree_test.go +++ b/pkg/tree/tree_test.go @@ -65,7 +65,8 @@ var _ = Describe("Tree", func() { solution, err := s.Install([]pkg.Package{pack}) Expect(err).ToNot(HaveOccurred()) - solution = solution.Order(generalRecipe.GetDatabase(), pack.GetFingerPrint()) + solution, err = solution.Order(generalRecipe.GetDatabase(), pack.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) Expect(solution[0].Package.GetName()).To(Equal("a")) Expect(solution[0].Value).To(BeFalse()) @@ -137,7 +138,8 @@ var _ = Describe("Tree", func() { solution, err := s.Install([]pkg.Package{Dd}) Expect(err).ToNot(HaveOccurred()) - solution = solution.Order(generalRecipe.GetDatabase(), Dd.GetFingerPrint()) + solution, err = solution.Order(generalRecipe.GetDatabase(), Dd.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) pack, err := generalRecipe.GetDatabase().FindPackage(&pkg.DefaultPackage{Name: "a", Category: "test", Version: "1.0"}) Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/versioner/interface.go b/pkg/versioner/interface.go new file mode 100644 index 00000000..74fbf0b8 --- /dev/null +++ b/pkg/versioner/interface.go @@ -0,0 +1,27 @@ +// Copyright © 2019-2020 Ettore Di Giacinto , +// Daniele Rondina +// +// 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 . + +package version + +// Versioner is responsible of sanitizing versions, +// validating them and ordering by precedence +type Versioner interface { + Sanitize(string) string + Validate(string) error + Sort([]string) []string + + ValidateSelector(version string, selector string) bool +} diff --git a/pkg/package/version.go b/pkg/versioner/version.go similarity index 90% rename from pkg/package/version.go rename to pkg/versioner/version.go index 5414db34..ff17b007 100644 --- a/pkg/package/version.go +++ b/pkg/versioner/version.go @@ -14,15 +14,14 @@ // You should have received a copy of the GNU General Public License along // with this program; if not, see . -package pkg +package version import ( - "errors" "fmt" "regexp" "strings" - version "github.com/hashicorp/go-version" + semver "github.com/hashicorp/go-version" ) // Package Selector Condition @@ -162,7 +161,7 @@ func ParseVersion(v string) (PkgVersionSelector, error) { } // Check if build number is present - buildIdx := strings.Index(v, "+") + buildIdx := strings.LastIndex(v, "+") buildVersion := "" if buildIdx > 0 { // ::= @@ -252,8 +251,8 @@ func ParseVersion(v string) (PkgVersionSelector, error) { } func PackageAdmit(selector, i PkgVersionSelector) (bool, error) { - var v1 *version.Version = nil - var v2 *version.Version = nil + var v1 *semver.Version = nil + var v2 *semver.Version = nil var ans bool var err error var sanitizedSelectorVersion, sanitizedIVersion string @@ -262,14 +261,14 @@ func PackageAdmit(selector, i PkgVersionSelector) (bool, error) { // TODO: This is temporary!. I promise it. sanitizedSelectorVersion = strings.ReplaceAll(selector.Version, "_", "-") - v1, err = version.NewVersion(sanitizedSelectorVersion) + v1, err = semver.NewVersion(sanitizedSelectorVersion) if err != nil { return false, err } } if i.Version != "" { sanitizedIVersion = strings.ReplaceAll(i.Version, "_", "-") - v2, err = version.NewVersion(sanitizedIVersion) + v2, err = semver.NewVersion(sanitizedIVersion) if err != nil { return false, err } @@ -307,7 +306,7 @@ func PackageAdmit(selector, i PkgVersionSelector) (bool, error) { segments[len(segments)-1]++ } nextVersion := strings.Trim(strings.Replace(fmt.Sprint(segments), " ", ".", -1), "[]") - constraints, err := version.NewConstraint( + constraints, err := semver.NewConstraint( fmt.Sprintf(">= %s, < %s", sanitizedSelectorVersion, nextVersion), ) if err != nil { @@ -335,35 +334,3 @@ func PackageAdmit(selector, i PkgVersionSelector) (bool, error) { 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) -} diff --git a/pkg/package/version_test.go b/pkg/versioner/version_test.go similarity index 99% rename from pkg/package/version_test.go rename to pkg/versioner/version_test.go index 02bb8cda..a5793399 100644 --- a/pkg/package/version_test.go +++ b/pkg/versioner/version_test.go @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License along // with this program; if not, see . -package pkg_test +package version_test import ( gentoo "github.com/Sabayon/pkgs-checker/pkg/gentoo" - . "github.com/mudler/luet/pkg/package" + . "github.com/mudler/luet/pkg/versioner" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/pkg/versioner/versioner.go b/pkg/versioner/versioner.go new file mode 100644 index 00000000..340f7553 --- /dev/null +++ b/pkg/versioner/versioner.go @@ -0,0 +1,148 @@ +// Copyright © 2019-2020 Ettore Di Giacinto , +// Daniele Rondina +// +// 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 . + +package version + +import ( + "errors" + "regexp" + "sort" + "strconv" + "strings" + + semver "github.com/hashicorp/go-version" + debversion "github.com/knqyf263/go-deb-version" +) + +// 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 +type WrappedVersioner struct{} + +func DefaultVersioner() Versioner { + return &WrappedVersioner{} +} + +func (w *WrappedVersioner) Validate(version string) error { + if !debversion.Valid(version) { + return errors.New("Invalid version") + } + return nil +} + +func (w *WrappedVersioner) ValidateSelector(version string, selector string) bool { + vS, err := ParseVersion(selector) + if err != nil { + return false + } + + vSI, err := ParseVersion(version) + if err != nil { + return false + } + ok, err := PackageAdmit(vS, vSI) + if err != nil { + return false + } + return ok +} + +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) +} + +func (w *WrappedVersioner) Sort(toSort []string) []string { + if len(toSort) == 0 { + return toSort + } + var versionsMap map[string]string = make(map[string]string) + versionsRaw := []string{} + result := []string{} + for _, v := range toSort { + sanitizedVersion := w.Sanitize(v) + versionsMap[sanitizedVersion] = v + 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) + vs[i] = v + } + + sort.Slice(vs, func(i, j int) bool { + return vs[i].LessThan(vs[j]) + }) + + for _, v := range vs { + result = append(result, versionsMap[v.String()]) + } + return result +} diff --git a/pkg/versioner/versioner_suite_test.go b/pkg/versioner/versioner_suite_test.go new file mode 100644 index 00000000..55fec9c0 --- /dev/null +++ b/pkg/versioner/versioner_suite_test.go @@ -0,0 +1,32 @@ +// Copyright © 2019 Ettore Di Giacinto +// +// 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 . + +package version_test + +import ( + "testing" + + . "github.com/mudler/luet/cmd" + config "github.com/mudler/luet/pkg/config" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestVersioner(t *testing.T) { + RegisterFailHandler(Fail) + LoadConfig(config.LuetCfg) + RunSpecs(t, "Versioner Suite") +} diff --git a/pkg/versioner/versioner_test.go b/pkg/versioner/versioner_test.go new file mode 100644 index 00000000..743f4609 --- /dev/null +++ b/pkg/versioner/versioner_test.go @@ -0,0 +1,82 @@ +// Copyright © 2019 Ettore Di Giacinto +// Daniele Rondina +// +// 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 . + +package version_test + +import ( + . "github.com/mudler/luet/pkg/versioner" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Versioner", func() { + Context("Invalid version", func() { + versioner := DefaultVersioner() + It("Sanitize", func() { + sanitized := versioner.Sanitize("foo_bar") + Expect(sanitized).Should(Equal("foo-bar")) + }) + }) + + Context("valid version", func() { + versioner := DefaultVersioner() + It("Validate", func() { + err := versioner.Validate("1.0") + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + + Context("invalid version", func() { + versioner := DefaultVersioner() + It("Validate", func() { + err := versioner.Validate("1.0_##") + Expect(err).Should(HaveOccurred()) + }) + }) + + Context("Sorting", func() { + versioner := DefaultVersioner() + It("finds the correct ordering", func() { + sorted := versioner.Sort([]string{"1.0", "0.1"}) + Expect(sorted).Should(Equal([]string{"0.1", "1.0"})) + }) + }) + + Context("Sorting with invalid characters", func() { + versioner := DefaultVersioner() + It("finds the correct ordering", func() { + sorted := versioner.Sort([]string{"1.0_1", "0.1"}) + Expect(sorted).Should(Equal([]string{"0.1", "1.0_1"})) + }) + }) + + Context("Complex Sorting", func() { + versioner := DefaultVersioner() + It("finds the correct ordering", func() { + sorted := versioner.Sort([]string{"1.0", "0.1", "0.22", "1.1", "1.9", "1.10", "11.1"}) + Expect(sorted).Should(Equal([]string{"0.1", "0.22", "1.0", "1.1", "1.9", "1.10", "11.1"})) + }) + }) + + // from: https://github.com/knqyf263/go-deb-version/blob/master/version_test.go#L8 + Context("Debian Sorting", func() { + versioner := DefaultVersioner() + It("finds the correct ordering", func() { + sorted := versioner.Sort([]string{"2:7.4.052-1ubuntu3.1", "2:7.4.052-1ubuntu1", "2:7.4.052-1ubuntu2", "2:7.4.052-1ubuntu3"}) + 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"})) + }) + }) +}) diff --git a/tests/fixtures/versioning/libsigc++-2/build.yaml b/tests/fixtures/versioning/libsigc++-2/build.yaml new file mode 100644 index 00000000..ea4f5029 --- /dev/null +++ b/tests/fixtures/versioning/libsigc++-2/build.yaml @@ -0,0 +1 @@ +image: "alpine" \ No newline at end of file diff --git a/tests/fixtures/versioning/libsigc++-2/definition.yaml b/tests/fixtures/versioning/libsigc++-2/definition.yaml new file mode 100644 index 00000000..cf0e5487 --- /dev/null +++ b/tests/fixtures/versioning/libsigc++-2/definition.yaml @@ -0,0 +1,8 @@ + +labels: null +category: dev-libs +conflicts: null +id: 0 +license: LGPL-2.1+ +name: libsigc++-2 +version: "2.10.1+1" diff --git a/tests/fixtures/versioning/libsndfile/build.yaml b/tests/fixtures/versioning/libsndfile/build.yaml new file mode 100644 index 00000000..b174b232 --- /dev/null +++ b/tests/fixtures/versioning/libsndfile/build.yaml @@ -0,0 +1 @@ +image: "alpine" diff --git a/tests/fixtures/versioning/libsndfile/definition.yaml b/tests/fixtures/versioning/libsndfile/definition.yaml new file mode 100644 index 00000000..5e5fa9d1 --- /dev/null +++ b/tests/fixtures/versioning/libsndfile/definition.yaml @@ -0,0 +1,7 @@ +category: media-libs +conflicts: null +id: 0 +license: LGPL-2.1 +name: libsndfile +#version: 1.0.29+1 +version: 1.0.29+pre2_p20191024.1 diff --git a/tests/integration/08_versioning.sh b/tests/integration/08_versioning.sh new file mode 100755 index 00000000..330e7dd4 --- /dev/null +++ b/tests/integration/08_versioning.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +export LUET_NOLOCK=true + +oneTimeSetUp() { +export tmpdir="$(mktemp -d)" +} + +oneTimeTearDown() { + rm -rf "$tmpdir" +} + +testBuild() { + mkdir $tmpdir/testbuild + luet build --tree "$ROOT_DIR/tests/fixtures/versioning" --destination $tmpdir/testbuild --compression gzip --all + buildst=$? + assertEquals 'builds successfully' "0" "$buildst" + + luet build --tree "$ROOT_DIR/tests/fixtures/versioning" --destination $tmpdir/testbuild --compression gzip media-libs/libsndfile + buildst=$? + assertEquals 'builds successfully' "0" "$buildst" + + + luet build --tree "$ROOT_DIR/tests/fixtures/versioning" --destination $tmpdir/testbuild --compression gzip '>=dev-libs/libsigc++-2-0' + buildst=$? + assertEquals 'builds successfully' "0" "$buildst" +} + +testRepo() { + assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]" + luet create-repo --tree "$ROOT_DIR/tests/fixtures/versioning" \ + --output $tmpdir/testbuild \ + --packages $tmpdir/testbuild \ + --name "test" \ + --descr "Test Repo" \ + --urls $tmpdir/testrootfs \ + --type disk > /dev/null + + createst=$? + assertEquals 'create repo successfully' "0" "$createst" + assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]" +} + +testConfig() { + mkdir $tmpdir/testrootfs + cat < $tmpdir/luet.yaml +general: + debug: true +system: + rootfs: $tmpdir/testrootfs + database_path: "/" + database_engine: "boltdb" +repositories: + - name: "main" + type: "disk" + enable: true + urls: + - "$tmpdir/testbuild" +EOF + luet config --config $tmpdir/luet.yaml + res=$? + assertEquals 'config test successfully' "0" "$res" +} + +testInstall() { + luet install --config $tmpdir/luet.yaml media-libs/libsndfile + installst=$? + assertEquals 'install test successfully' "0" "$installst" +} + +testInstall2() { + luet install --config $tmpdir/luet.yaml '>=dev-libs/libsigc++-2-0' + installst=$? + assertEquals 'install test successfully' "0" "$installst" +} + + +testCleanup() { + luet cleanup --config $tmpdir/luet.yaml + installst=$? + assertEquals 'install test successfully' "0" "$installst" +} + +# Load shUnit2. +. "$ROOT_DIR/tests/integration/shunit2"/shunit2 diff --git a/vendor/github.com/Sabayon/pkgs-checker/pkg/gentoo/pkg.go b/vendor/github.com/Sabayon/pkgs-checker/pkg/gentoo/pkg.go index 59261489..6844b412 100644 --- a/vendor/github.com/Sabayon/pkgs-checker/pkg/gentoo/pkg.go +++ b/vendor/github.com/Sabayon/pkgs-checker/pkg/gentoo/pkg.go @@ -56,7 +56,7 @@ const ( const ( RegexCatString = `(^[a-z]+[0-9]*[a-z]*[-]*[a-z]+[0-9]*[a-z]*|^virtual)` - RegexPkgNameString = `([a-zA-Z]*[0-9a-zA-Z\.\-_]*[a-zA-Z0-9]+|[a-zA-Z]+[+]+)` + RegexPkgNameString = `([a-zA-Z]*[0-9a-zA-Z\.\-_]*[a-zA-Z0-9]+|[a-zA-Z\-]+[+]+[-]+[0-9a-zA-Z\.]*|[a-zA-Z\-]+[+]+)` ) type GentooPackage struct { diff --git a/vendor/github.com/knqyf263/go-deb-version/.gitignore b/vendor/github.com/knqyf263/go-deb-version/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/vendor/github.com/knqyf263/go-deb-version/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/knqyf263/go-deb-version/.travis.yml b/vendor/github.com/knqyf263/go-deb-version/.travis.yml new file mode 100644 index 00000000..3e0417b1 --- /dev/null +++ b/vendor/github.com/knqyf263/go-deb-version/.travis.yml @@ -0,0 +1,10 @@ +language: go + +go: + - 1.7 + - 1.8 +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken ca4cRbwqOXDZBv39XjjxoXDDUnwEA7klw diff --git a/vendor/github.com/knqyf263/go-deb-version/LICENSE b/vendor/github.com/knqyf263/go-deb-version/LICENSE new file mode 100644 index 00000000..c369be2b --- /dev/null +++ b/vendor/github.com/knqyf263/go-deb-version/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Teppei Fukuda + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/knqyf263/go-deb-version/README.md b/vendor/github.com/knqyf263/go-deb-version/README.md new file mode 100644 index 00000000..ed0e4d53 --- /dev/null +++ b/vendor/github.com/knqyf263/go-deb-version/README.md @@ -0,0 +1,72 @@ +# go-deb-version + +[![Build Status](https://travis-ci.org/knqyf263/go-deb-version.svg?branch=master)](https://travis-ci.org/knqyf263/go-deb-version) +[![Coverage Status](https://coveralls.io/repos/github/knqyf263/go-deb-version/badge.svg)](https://coveralls.io/github/knqyf263/go-deb-version) +[![Go Report Card](https://goreportcard.com/badge/github.com/knqyf263/go-deb-version)](https://goreportcard.com/report/github.com/knqyf263/go-deb-version) +[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/knqyf263/go-deb-version/blob/master/LICENSE) + +A Go library for parsing package versions + +go-deb-version is a library for parsing and comparing versions + +Versions used with go-deb-version must follow [deb-version](http://man.he.net/man5/deb-version) (ex. 2:6.0-9ubuntu1) +The implementation is based on [Debian Policy Manual](https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version) + +OS: Debian, Ubnutu + + +# Installation and Usage + +Installation can be done with a normal go get: + +``` +$ go get github.com/knqyf263/go-deb-version +``` + +## Version Parsing and Comparison + +``` +import "github.com/knqyf263/go-deb-version" + +v1, err := version.NewVersion("2:6.0-9") +v2, err := version.NewVersion("2:6.0-9ubuntu1") + +// Comparison example. There is also GreaterThan, Equal. +if v1.LessThan(v2) { + fmt.Printf("%s is less than %s", v1, v2) +} +``` + +## Version Sorting + +``` +raw := []string{"7.4.052-1ubuntu3.1", "7.4.052-1ubuntu3", "7.1-022+1ubuntu1", "7.1.291-1", "7.3.000+hg~ee53a39d5896-1"} +vs := make([]version.Version, len(raw)) +for i, r := range raw { + v, _ := version.NewVersion(r) + vs[i] = v +} + +sort.Slice(vs, func(i, j int) bool { + return vs[i].LessThan(vs[j]) +}) +``` + +# Contribute + +1. fork a repository: github.com/knqyf263/go-deb-version to github.com/you/repo +2. get original code: `go get github.com/knqyf263/go-deb-version` +3. work on original code +4. add remote to your repo: git remote add myfork https://github.com/you/repo.git +5. push your changes: git push myfork +6. create a new Pull Request + +- see [GitHub and Go: forking, pull requests, and go-getting](http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html) + +---- + +# License +MIT + +# Author +Teppei Fukuda diff --git a/vendor/github.com/knqyf263/go-deb-version/version.go b/vendor/github.com/knqyf263/go-deb-version/version.go new file mode 100644 index 00000000..e99d2e97 --- /dev/null +++ b/vendor/github.com/knqyf263/go-deb-version/version.go @@ -0,0 +1,267 @@ +package version + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "strconv" + "strings" + "unicode" +) + +type defaultNumSlice []int + +// get function returns 0, if the slice does not have the specified index. +func (n defaultNumSlice) get(i int) int { + if len(n) > i { + return n[i] + } + return 0 +} + +type defaultStringSlice []string + +// get function returns "", if the slice does not have the specified index. +func (s defaultStringSlice) get(i int) string { + if len(s) > i { + return s[i] + } + return "" +} + +// Version represents a package version (http://man.he.net/man5/deb-version). +type Version struct { + epoch int + upstreamVersion string + debianRevision string +} + +var ( + digitRegexp = regexp.MustCompile(`[0-9]+`) + nonDigitRegexp = regexp.MustCompile(`[^0-9]+`) +) + +// NewVersion returns a parsed version +func NewVersion(ver string) (version Version, err error) { + // Trim space + ver = strings.TrimSpace(ver) + + // Parse epoch + splitted := strings.SplitN(ver, ":", 2) + if len(splitted) == 1 { + version.epoch = 0 + ver = splitted[0] + } else { + version.epoch, err = strconv.Atoi(splitted[0]) + if err != nil { + return Version{}, fmt.Errorf("epoch parse error: %v", err) + } + + if version.epoch < 0 { + return Version{}, errors.New("epoch is negative") + } + ver = splitted[1] + } + + // Parse upstream_version and debian_revision + index := strings.LastIndex(ver, "-") + if index >= 0 { + version.upstreamVersion = ver[:index] + version.debianRevision = ver[index+1:] + + } else { + version.upstreamVersion = ver + } + + // Verify upstream_version is valid + err = verifyUpstreamVersion(version.upstreamVersion) + if err != nil { + return Version{}, err + } + + // Verify debian_revision is valid + err = verifyDebianRevision(version.debianRevision) + if err != nil { + return Version{}, err + } + + return version, nil +} + +func verifyUpstreamVersion(str string) error { + if len(str) == 0 { + return errors.New("upstream_version is empty") + } + + // The upstream-version should start with a digit + if !unicode.IsDigit(rune(str[0])) { + return errors.New("upstream_version must start with digit") + } + + // The upstream-version may contain only alphanumerics("A-Za-z0-9") and the characters .+-:~ + allowedSymbols := ".-+~:_" + for _, s := range str { + if !unicode.IsDigit(s) && !unicode.IsLetter(s) && !strings.ContainsRune(allowedSymbols, s) { + return errors.New("upstream_version includes invalid character") + } + } + return nil +} + +func verifyDebianRevision(str string) error { + // The debian-revision may contain only alphanumerics and the characters +.~ + allowedSymbols := "+.~_" + for _, s := range str { + if !unicode.IsDigit(s) && !unicode.IsLetter(s) && !strings.ContainsRune(allowedSymbols, s) { + return errors.New("debian_revision includes invalid character") + } + } + return nil +} + +// Valid validates the version +func Valid(ver string) bool { + _, err := NewVersion(ver) + return err == nil +} + +// Equal returns whether this version is equal with another version. +func (v1 *Version) Equal(v2 Version) bool { + return v1.Compare(v2) == 0 +} + +// GreaterThan returns whether this version is greater than another version. +func (v1 *Version) GreaterThan(v2 Version) bool { + return v1.Compare(v2) > 0 +} + +// LessThan returns whether this version is less than another version. +func (v1 Version) LessThan(v2 Version) bool { + return v1.Compare(v2) < 0 +} + +// Compare returns an integer comparing two version according to deb-version. +// The result will be 0 if v1==v2, -1 if v1 < v2, and +1 if v1 > v2. +func (v1 Version) Compare(v2 Version) int { + // Equal + if reflect.DeepEqual(v1, v2) { + return 0 + } + + // Compare epochs + if v1.epoch > v2.epoch { + return 1 + } else if v1.epoch < v2.epoch { + return -1 + } + + // Compare version + ret := compare(v1.upstreamVersion, v2.upstreamVersion) + if ret != 0 { + return ret + } + + //Compare debian_revision + return compare(v1.debianRevision, v2.debianRevision) +} + +// String returns the full version string +func (v1 Version) String() string { + version := "" + if v1.epoch > 0 { + version += fmt.Sprintf("%d:", v1.epoch) + } + version += v1.upstreamVersion + + if v1.debianRevision != "" { + version += fmt.Sprintf("-%s", v1.debianRevision) + + } + return version +} + +func compare(v1, v2 string) int { + // Equal + if v1 == v2 { + return 0 + } + + // Extract digit strings and non-digit strings + numbers1, strings1 := extract(v1) + numbers2, strings2 := extract(v2) + + if len(v1) > 0 && unicode.IsDigit(rune(v1[0])) { + strings1 = append([]string{""}, strings1...) + } + if len(v2) > 0 && unicode.IsDigit(rune(v2[0])) { + strings2 = append([]string{""}, strings2...) + } + + for i := 0; ; i++ { + // Compare non-digit strings + diff := compareString(strings1.get(i), strings2.get(i)) + if diff != 0 { + return diff + } + + // Compare digit strings + diff = numbers1.get(i) - numbers2.get(i) + if diff != 0 { + return diff + } + } +} + +func compareString(s1, s2 string) int { + if s1 == s2 { + return 0 + } + + for i := 0; ; i++ { + a := 0 + if i < len(s1) { + a = order(rune(s1[i])) + } + + b := 0 + if i < len(s2) { + b = order(rune(s2[i])) + } + + if a != b { + return a - b + } + } + +} + +// order function returns the number corresponding to rune +func order(r rune) int { + // all the letters sort earlier than all the non-letters + if unicode.IsLetter(r) { + return int(r) + } + + // a tilde sorts before anything + if r == '~' { + return -1 + } + + return int(r) + 256 +} + +func extract(version string) (defaultNumSlice, defaultStringSlice) { + numbers := digitRegexp.FindAllString(version, -1) + + var dnum defaultNumSlice + for _, number := range numbers { + n, _ := strconv.Atoi(number) + dnum = append(dnum, n) + } + + s := nonDigitRegexp.FindAllString(version, -1) + + return dnum, defaultStringSlice(s) + +} diff --git a/vendor/modules.txt b/vendor/modules.txt index dc8bfe47..43c984fa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -23,7 +23,7 @@ github.com/Microsoft/hcsshim/internal/longpath github.com/Microsoft/hcsshim/internal/safefile # github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 github.com/Nvveen/Gotty -# github.com/Sabayon/pkgs-checker v0.6.2-0.20200315232328-b6efed54b4b1 +# github.com/Sabayon/pkgs-checker v0.6.2-0.20200404093625-076438c31739 github.com/Sabayon/pkgs-checker/pkg/gentoo # github.com/apex/log v1.1.1 github.com/apex/log @@ -141,6 +141,8 @@ github.com/klauspost/compress/flate github.com/klauspost/cpuid # github.com/klauspost/pgzip v1.2.1 github.com/klauspost/pgzip +# github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d +github.com/knqyf263/go-deb-version # github.com/konsorten/go-windows-terminal-sequences v1.0.2 github.com/konsorten/go-windows-terminal-sequences # github.com/kyokomi/emoji v2.1.0+incompatible