Compare commits

..

7 Commits
0.7.4 ... 0.7.5

Author SHA1 Message Date
Ettore Di Giacinto
886cbd0036 Tag 0.7.5 2020-04-30 22:50:12 +02:00
Ettore Di Giacinto
b91288153a Make tree validation cmd concurrent 2020-04-30 21:48:46 +02:00
Ettore Di Giacinto
9cb290b484 Improve database errors 2020-04-30 20:44:34 +02:00
Ettore Di Giacinto
322ac99f17 Annotate package runtime definition when reclaiming
This was an issue as we were copying the buildspec instead
2020-04-30 18:56:50 +02:00
Ettore Di Giacinto
6acc5fc97e Make BuildFormula support dependency cycles 2020-04-30 18:56:35 +02:00
Ettore Di Giacinto
6a4557a3b3 Make RequireContains support dependency cycles 2020-04-30 18:56:06 +02:00
Ettore Di Giacinto
fb3c568051 Add development version 2020-04-24 19:46:35 +02:00
6 changed files with 246 additions and 177 deletions

View File

@@ -38,7 +38,7 @@ var Verbose bool
var LockedCommands = []string{"install", "uninstall", "upgrade"} var LockedCommands = []string{"install", "uninstall", "upgrade"}
const ( const (
LuetCLIVersion = "0.7.4" LuetCLIVersion = "0.7.5"
LuetEnvPrefix = "LUET" LuetEnvPrefix = "LUET"
) )

View File

@@ -17,11 +17,15 @@
package cmd_tree package cmd_tree
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"regexp"
"sort" "sort"
"strconv"
"sync"
//. "github.com/mudler/luet/pkg/config" . "github.com/mudler/luet/pkg/config"
helpers "github.com/mudler/luet/pkg/helpers" helpers "github.com/mudler/luet/pkg/helpers"
. "github.com/mudler/luet/pkg/logger" . "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package" pkg "github.com/mudler/luet/pkg/package"
@@ -31,37 +35,21 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func NewTreeValidateCommand() *cobra.Command { func validateWorker(i int,
var excludes []string wg *sync.WaitGroup,
var matches []string c <-chan pkg.Package,
var treePaths []string reciper tree.Builder,
withSolver bool,
regExcludes, regMatches []*regexp.Regexp,
excludes, matches []string,
errs chan error) {
defer wg.Done()
var ans = &cobra.Command{
Use: "validate [OPTIONS]",
Short: "Validate a tree or a list of packages",
Args: cobra.OnlyValidArgs,
PreRun: func(cmd *cobra.Command, args []string) {
if len(treePaths) < 1 {
Fatal("Mandatory tree param missing.")
}
},
Run: func(cmd *cobra.Command, args []string) {
var depSolver solver.PackageSolver var depSolver solver.PackageSolver
var errstr string
errors := make([]string, 0)
brokenPkgs := 0 brokenPkgs := 0
brokenDeps := 0 brokenDeps := 0
withSolver, _ := cmd.Flags().GetBool("with-solver") var errstr string
reciper := tree.NewInstallerRecipe(pkg.NewInMemoryDatabase(false))
for _, treePath := range treePaths {
err := reciper.Load(treePath)
if err != nil {
Fatal("Error on load tree ", err)
}
}
emptyInstallationDb := pkg.NewInMemoryDatabase(false) emptyInstallationDb := pkg.NewInMemoryDatabase(false)
if withSolver { if withSolver {
@@ -70,17 +58,7 @@ func NewTreeValidateCommand() *cobra.Command {
emptyInstallationDb) emptyInstallationDb)
} }
regExcludes, err := helpers.CreateRegexArray(excludes) for p := range c {
if err != nil {
Fatal(err.Error())
}
regMatches, err := helpers.CreateRegexArray(matches)
if err != nil {
Fatal(err.Error())
}
for _, p := range reciper.GetDatabase().World() {
found, err := reciper.GetDatabase().FindPackages( found, err := reciper.GetDatabase().FindPackages(
&pkg.DefaultPackage{ &pkg.DefaultPackage{
Name: p.GetName(), Name: p.GetName(),
@@ -100,7 +78,7 @@ func NewTreeValidateCommand() *cobra.Command {
errstr, errstr,
)) ))
errors = append(errors, errs <- errors.New(
fmt.Sprintf("%s/%s-%s: Broken. No versions could be found by database %s", fmt.Sprintf("%s/%s-%s: Broken. No versions could be found by database %s",
p.GetCategory(), p.GetName(), p.GetVersion(), p.GetCategory(), p.GetName(), p.GetVersion(),
errstr, errstr,
@@ -172,7 +150,7 @@ func NewTreeValidateCommand() *cobra.Command {
errstr, errstr,
)) ))
errors = append(errors, errs <- errors.New(
fmt.Sprintf("%s/%s-%s: Broken Dep %s/%s-%s - %s", fmt.Sprintf("%s/%s-%s: Broken Dep %s/%s-%s - %s",
p.GetCategory(), p.GetName(), p.GetVersion(), p.GetCategory(), p.GetName(), p.GetVersion(),
r.GetCategory(), r.GetName(), r.GetVersion(), r.GetCategory(), r.GetName(), r.GetVersion(),
@@ -204,7 +182,7 @@ func NewTreeValidateCommand() *cobra.Command {
err.Error(), err.Error(),
)) ))
errors = append(errors, errs <- errors.New(
fmt.Sprintf("%s/%s-%s: solver broken for Dep %s/%s-%s - %s", fmt.Sprintf("%s/%s-%s: solver broken for Dep %s/%s-%s - %s",
p.GetCategory(), p.GetName(), p.GetVersion(), p.GetCategory(), p.GetName(), p.GetVersion(),
r.GetCategory(), r.GetName(), r.GetVersion(), r.GetCategory(), r.GetName(), r.GetVersion(),
@@ -223,16 +201,82 @@ func NewTreeValidateCommand() *cobra.Command {
brokenPkgs++ brokenPkgs++
} }
} }
}
sort.Strings(errors) func NewTreeValidateCommand() *cobra.Command {
for _, e := range errors { var excludes []string
var matches []string
var treePaths []string
var ans = &cobra.Command{
Use: "validate [OPTIONS]",
Short: "Validate a tree or a list of packages",
Args: cobra.OnlyValidArgs,
PreRun: func(cmd *cobra.Command, args []string) {
if len(treePaths) < 1 {
Fatal("Mandatory tree param missing.")
}
},
Run: func(cmd *cobra.Command, args []string) {
concurrency := LuetCfg.GetGeneral().Concurrency
withSolver, _ := cmd.Flags().GetBool("with-solver")
reciper := tree.NewInstallerRecipe(pkg.NewInMemoryDatabase(false))
for _, treePath := range treePaths {
err := reciper.Load(treePath)
if err != nil {
Fatal("Error on load tree ", err)
}
}
regExcludes, err := helpers.CreateRegexArray(excludes)
if err != nil {
Fatal(err.Error())
}
regMatches, err := helpers.CreateRegexArray(matches)
if err != nil {
Fatal(err.Error())
}
all := make(chan pkg.Package)
errs := make(chan error)
var wg = new(sync.WaitGroup)
for i := 0; i < concurrency; i++ {
wg.Add(1)
go validateWorker(i, wg, all,
reciper, withSolver, regExcludes, regMatches, excludes, matches,
errs)
}
for _, p := range reciper.GetDatabase().World() {
all <- p
}
close(all)
// Wait separately and once done close the channel
go func() {
wg.Wait()
close(errs)
}()
stringerrs := []string{}
for e := range errs {
stringerrs = append(stringerrs, e.Error())
}
sort.Strings(stringerrs)
for _, e := range stringerrs {
fmt.Println(e) fmt.Println(e)
} }
fmt.Println("Broken packages:", brokenPkgs, "(", brokenDeps, "deps ).")
if brokenPkgs > 0 { // fmt.Println("Broken packages:", brokenPkgs, "(", brokenDeps, "deps ).")
os.Exit(1) if len(stringerrs) != 0 {
Fatal("Errors: " + strconv.Itoa(len(stringerrs)))
// if brokenPkgs > 0 {
//os.Exit(1)
} else { } else {
Info("All good! :white_check_mark:")
os.Exit(0) os.Exit(0)
} }
}, },

View File

@@ -222,7 +222,11 @@ func (l *LuetInstaller) Reclaim(s *System) error {
FILES: FILES:
for _, f := range artefact.GetFiles() { for _, f := range artefact.GetFiles() {
if helpers.Exists(filepath.Join(s.Target, f)) { if helpers.Exists(filepath.Join(s.Target, f)) {
toMerge = append(toMerge, ArtifactMatch{Artifact: artefact, Package: artefact.GetCompileSpec().GetPackage()}) p, err := repo.GetTree().GetDatabase().FindPackage(artefact.GetCompileSpec().GetPackage())
if err != nil {
return err
}
toMerge = append(toMerge, ArtifactMatch{Artifact: artefact, Package: p})
break FILES break FILES
} }
} }

View File

@@ -17,6 +17,7 @@ package pkg
import ( import (
"encoding/base64" "encoding/base64"
"fmt"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
@@ -228,7 +229,7 @@ func (db *BoltDatabase) getProvide(p Package) (Package, error) {
db.Unlock() db.Unlock()
if !ok { if !ok {
return nil, errors.New("No versions found for package") return nil, errors.New(fmt.Sprintf("No versions found for: %s", p.HumanReadableString()))
} }
for ve, _ := range versions { for ve, _ := range versions {
@@ -240,7 +241,7 @@ func (db *BoltDatabase) getProvide(p Package) (Package, error) {
if match { if match {
pa, ok := db.ProvidesDatabase[p.GetPackageName()][ve] pa, ok := db.ProvidesDatabase[p.GetPackageName()][ve]
if !ok { if !ok {
return nil, errors.New("No versions found for package") return nil, errors.New(fmt.Sprintf("No versions found for: %s", p.HumanReadableString()))
} }
return pa, nil //pick the first (we shouldn't have providers that are conflicting) return pa, nil //pick the first (we shouldn't have providers that are conflicting)
// TODO: A find dbcall here would recurse, but would give chance to have providers of providers // TODO: A find dbcall here would recurse, but would give chance to have providers of providers
@@ -309,7 +310,7 @@ func (db *BoltDatabase) RemovePackage(p Package) error {
var found DefaultPackage var found DefaultPackage
err = bolt.Select(q.Eq("Name", p.GetName()), q.Eq("Category", p.GetCategory()), q.Eq("Version", p.GetVersion())).Limit(1).Delete(&found) err = bolt.Select(q.Eq("Name", p.GetName()), q.Eq("Category", p.GetCategory()), q.Eq("Version", p.GetVersion())).Limit(1).Delete(&found)
if err != nil { if err != nil {
return errors.Wrap(err, "No package found to delete") return errors.New(fmt.Sprintf("Package not found: %s", p.HumanReadableString()))
} }
return nil return nil
} }

View File

@@ -18,6 +18,7 @@ package pkg
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"regexp" "regexp"
"sync" "sync"
@@ -59,7 +60,7 @@ func (db *InMemoryDatabase) Get(s string) (string, error) {
defer db.Unlock() defer db.Unlock()
pa, ok := db.Database[s] pa, ok := db.Database[s]
if !ok { if !ok {
return "", errors.New("No key found with that id") return "", errors.New(fmt.Sprintf("No key found for: %s", s))
} }
return pa, nil return pa, nil
} }
@@ -248,7 +249,7 @@ func (db *InMemoryDatabase) FindPackages(p Package) (Packages, error) {
} }
versions, ok := db.CacheNoVersion[p.GetPackageName()] versions, ok := db.CacheNoVersion[p.GetPackageName()]
if !ok { if !ok {
return nil, errors.New("No versions found for package") return nil, errors.New(fmt.Sprintf("No versions found for: %s", p.HumanReadableString()))
} }
var versionsInWorld []Package var versionsInWorld []Package
for ve, _ := range versions { for ve, _ := range versions {
@@ -277,7 +278,7 @@ func (db *InMemoryDatabase) UpdatePackage(p Package) error {
return db.Set(p.GetFingerPrint(), enc) return db.Set(p.GetFingerPrint(), enc)
return errors.New("Package not found") return errors.New(fmt.Sprintf("Package not found: %s", p.HumanReadableString()))
} }
func (db *InMemoryDatabase) GetPackages() []string { func (db *InMemoryDatabase) GetPackages() []string {
@@ -302,7 +303,7 @@ func (db *InMemoryDatabase) GetPackageFiles(p Package) ([]string, error) {
pa, ok := db.FileDatabase[p.GetFingerPrint()] pa, ok := db.FileDatabase[p.GetFingerPrint()]
if !ok { if !ok {
return pa, errors.New("No key found with that id") return pa, errors.New(fmt.Sprintf("No key found for: %s", p.HumanReadableString()))
} }
return pa, nil return pa, nil

View File

@@ -480,7 +480,11 @@ func DecodePackage(ID string, db PackageDatabase) (Package, error) {
return db.GetPackage(ID) return db.GetPackage(ID)
} }
func (pack *DefaultPackage) RequiresContains(definitiondb PackageDatabase, s Package) (bool, error) { func (pack *DefaultPackage) scanRequires(definitiondb PackageDatabase, s Package, visited map[string]interface{}) (bool, error) {
if _, ok := visited[pack.HumanReadableString()]; ok {
return false, nil
}
visited[pack.HumanReadableString()] = true
p, err := definitiondb.FindPackage(pack) p, err := definitiondb.FindPackage(pack)
if err != nil { if err != nil {
p = pack //relax things p = pack //relax things
@@ -498,7 +502,7 @@ func (pack *DefaultPackage) RequiresContains(definitiondb PackageDatabase, s Pac
return true, nil return true, nil
} }
} }
if contains, err := re.RequiresContains(definitiondb, s); err == nil && contains { if contains, err := re.scanRequires(definitiondb, s, visited); err == nil && contains {
return true, nil return true, nil
} }
} }
@@ -506,6 +510,12 @@ func (pack *DefaultPackage) RequiresContains(definitiondb PackageDatabase, s Pac
return false, nil return false, nil
} }
// RequiresContains recursively scans into the database packages dependencies to find a match with the given package
// It is used by the solver during uninstall.
func (pack *DefaultPackage) RequiresContains(definitiondb PackageDatabase, s Package) (bool, error) {
return pack.scanRequires(definitiondb, s, make(map[string]interface{}))
}
// Best returns the best version of the package (the most bigger) from a list // 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 // 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 // It defaults to version.WrappedVersioner which supports both semver and debian versioning
@@ -540,7 +550,11 @@ func (set Packages) Unique() Packages {
return result return result
} }
func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db PackageDatabase) ([]bf.Formula, error) { func (pack *DefaultPackage) buildFormula(definitiondb PackageDatabase, db PackageDatabase, visited map[string]interface{}) ([]bf.Formula, error) {
if _, ok := visited[pack.HumanReadableString()]; ok {
return nil, nil
}
visited[pack.HumanReadableString()] = true
p, err := definitiondb.FindPackage(pack) p, err := definitiondb.FindPackage(pack)
if err != nil { if err != nil {
p = pack // Relax failures and trust the def p = pack // Relax failures and trust the def
@@ -642,8 +656,8 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
} }
B := bf.Var(encodedB) B := bf.Var(encodedB)
formulas = append(formulas, bf.Or(bf.Not(A), B)) formulas = append(formulas, bf.Or(bf.Not(A), B))
r := required.(*DefaultPackage) // We know since the implementation is DefaultPackage, that can be only DefaultPackage
f, err := required.BuildFormula(definitiondb, db) f, err := r.buildFormula(definitiondb, db, visited)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -672,8 +686,8 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
B := bf.Var(encodedB) B := bf.Var(encodedB)
formulas = append(formulas, bf.Or(bf.Not(A), formulas = append(formulas, bf.Or(bf.Not(A),
bf.Not(B))) bf.Not(B)))
r := p.(*DefaultPackage) // We know since the implementation is DefaultPackage, that can be only DefaultPackage
f, err := p.BuildFormula(definitiondb, db) f, err := r.buildFormula(definitiondb, db, visited)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -692,7 +706,8 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
formulas = append(formulas, bf.Or(bf.Not(A), formulas = append(formulas, bf.Or(bf.Not(A),
bf.Not(B))) bf.Not(B)))
f, err := required.BuildFormula(definitiondb, db) r := required.(*DefaultPackage) // We know since the implementation is DefaultPackage, that can be only DefaultPackage
f, err := r.buildFormula(definitiondb, db, visited)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -703,6 +718,10 @@ func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db Packag
return formulas, nil return formulas, nil
} }
func (pack *DefaultPackage) BuildFormula(definitiondb PackageDatabase, db PackageDatabase) ([]bf.Formula, error) {
return pack.buildFormula(definitiondb, db, make(map[string]interface{}))
}
func (p *DefaultPackage) Explain() { func (p *DefaultPackage) Explain() {
fmt.Println("====================") fmt.Println("====================")