diff --git a/main.go b/main.go index d18cf4aa..cbf48921 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,6 @@ import ( "github.com/leonelquinteros/gotext" - "github.com/Jguer/yay/v12/pkg/db/ialpm" "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/parser" @@ -109,26 +108,15 @@ func main() { return } - dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, run.Logger.Child("db")) - if err != nil { - if str := err.Error(); str != "" { - fallbackLog.Errorln(str) - } - - ret = 1 - - return - } - defer func() { if rec := recover(); rec != nil { fallbackLog.Errorln(rec, string(debug.Stack())) } - dbExecutor.Cleanup() + run.DB.Cleanup() }() - if err = handleCmd(ctx, run, cmdArgs, dbExecutor); err != nil { + if err = handleCmd(ctx, run, cmdArgs, run.DB); err != nil { if str := err.Error(); str != "" { fallbackLog.Errorln(str) if cmdArgs.ExistsArg("c") && cmdArgs.ExistsArg("y") && cmdArgs.Op == "S" { diff --git a/pkg/cmd/graph/main.go b/pkg/cmd/graph/main.go index 5b1b5744..b64d3325 100644 --- a/pkg/cmd/graph/main.go +++ b/pkg/cmd/graph/main.go @@ -4,16 +4,13 @@ import ( "context" "fmt" "os" - "path/filepath" - "github.com/Jguer/yay/v12/pkg/db/ialpm" "github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" - "github.com/Jguer/aur/metadata" "github.com/leonelquinteros/gotext" "github.com/pkg/errors" ) @@ -34,23 +31,7 @@ func handleCmd(logger *text.Logger) error { return err } - dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, logger) - if err != nil { - return err - } - - aurCache, err := metadata.New( - metadata.WithCacheFilePath( - filepath.Join(cfg.BuildDir, "aur.json"))) - if err != nil { - return errors.Wrap(err, gotext.Get("failed to retrieve aur Cache")) - } - - grapher := dep.NewGrapher(dbExecutor, cfg, aurCache, run.CmdBuilder, true, settings.NoConfirm, - cmdArgs.ExistsDouble("d", "nodeps"), false, false, - run.Logger.Child("grapher")) - - return graphPackage(context.Background(), grapher, cmdArgs.Targets) + return graphPackage(context.Background(), run.Grapher, cmdArgs.Targets) } func main() { @@ -70,7 +51,7 @@ func graphPackage( return errors.New(gotext.Get("only one target is allowed")) } - graph, err := grapher.GraphFromAUR(ctx, nil, []string{targets[0]}) + graph, err := grapher.GraphFromTargets(ctx, nil, []string{targets[0]}) if err != nil { return err } diff --git a/pkg/dep/aur.go b/pkg/dep/aur.go new file mode 100644 index 00000000..50365e52 --- /dev/null +++ b/pkg/dep/aur.go @@ -0,0 +1,479 @@ +package dep + +import ( + "context" + "fmt" + "strconv" + + aurc "github.com/Jguer/aur" + alpm "github.com/Jguer/go-alpm/v2" + mapset "github.com/deckarep/golang-set/v2" + "github.com/leonelquinteros/gotext" + + "github.com/Jguer/yay/v12/pkg/db" + "github.com/Jguer/yay/v12/pkg/dep/topo" + aur "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/settings" + "github.com/Jguer/yay/v12/pkg/text" + "github.com/Jguer/yay/v12/pkg/vcs" +) + +type AURHandler struct { + log *text.Logger + db db.Executor + cfg *settings.Configuration + vcsStore vcs.Store + + foundPkgs []string + + providerCache map[string][]aur.Pkg + dbExecutor db.Executor + aurClient aurc.QueryClient + fullGraph bool // If true, the graph will include all dependencies including already installed ones or repo + noConfirm bool // If true, the graph will not prompt for confirmation + noDeps bool // If true, the graph will not include dependencies + noCheckDeps bool // If true, the graph will not include check dependencies + needed bool // If true, the graph will only include packages that are not installed +} + +func (h *AURHandler) Test(target Target) bool { + // FIXME: add test + h.foundPkgs = append(h.foundPkgs, target.Name) + return true +} + +func (h *AURHandler) Graph(ctx context.Context, graph *topo.Graph[string, *InstallInfo]) error { + var errA error + _, errA = h.GraphFromAUR(ctx, graph, h.foundPkgs) + if errA != nil { + return errA + } + return nil +} + +func (h *AURHandler) AddDepsForPkgs(ctx context.Context, pkgs []*aur.Pkg, graph *topo.Graph[string, *InstallInfo]) { + for _, pkg := range pkgs { + h.addDepNodes(ctx, pkg, graph) + } +} + +func (h *AURHandler) addDepNodes(ctx context.Context, pkg *aur.Pkg, graph *topo.Graph[string, *InstallInfo]) { + if len(pkg.MakeDepends) > 0 { + h.addNodes(ctx, graph, pkg.Name, pkg.MakeDepends, MakeDep) + } + + if !h.noDeps && len(pkg.Depends) > 0 { + h.addNodes(ctx, graph, pkg.Name, pkg.Depends, Dep) + } + + if !h.noCheckDeps && !h.noDeps && len(pkg.CheckDepends) > 0 { + h.addNodes(ctx, graph, pkg.Name, pkg.CheckDepends, CheckDep) + } +} + +func (h *AURHandler) GraphAURTarget(ctx context.Context, + graph *topo.Graph[string, *InstallInfo], + pkg *aurc.Pkg, instalInfo *InstallInfo, +) *topo.Graph[string, *InstallInfo] { + if graph == nil { + graph = NewGraph() + } + + graph.AddNode(pkg.Name) + + h.AddAurPkgProvides(pkg, graph) + + validateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{ + Color: colorMap[instalInfo.Reason], + Background: bgColorMap[AUR], + Value: instalInfo, + }) + + return graph +} + +func (h *AURHandler) GraphFromAUR(ctx context.Context, + graph *topo.Graph[string, *InstallInfo], + targets []string, +) (*topo.Graph[string, *InstallInfo], error) { + if graph == nil { + graph = NewGraph() + } + + if len(targets) == 0 { + return graph, nil + } + + aurPkgs, errCache := h.aurClient.Get(ctx, &aurc.Query{By: aurc.Name, Needles: targets}) + if errCache != nil { + h.log.Errorln(errCache) + } + + for i := range aurPkgs { + pkg := &aurPkgs[i] + if _, ok := h.providerCache[pkg.Name]; !ok { + h.providerCache[pkg.Name] = []aurc.Pkg{*pkg} + } + } + + aurPkgsAdded := []*aurc.Pkg{} + + for _, target := range targets { + if cachedProvidePkg, ok := h.providerCache[target]; ok { + aurPkgs = cachedProvidePkg + } else { + var errA error + aurPkgs, errA = h.aurClient.Get(ctx, &aurc.Query{By: aurc.Provides, Needles: []string{target}, Contains: true}) + if errA != nil { + h.log.Errorln(gotext.Get("Failed to find AUR package for"), " ", target, ":", errA) + } + } + + if len(aurPkgs) == 0 { + h.log.Errorln(gotext.Get("No AUR package found for"), " ", target) + + continue + } + + aurPkg := &aurPkgs[0] + if len(aurPkgs) > 1 { + chosen := h.provideMenu(target, aurPkgs) + aurPkg = chosen + h.providerCache[target] = []aurc.Pkg{*aurPkg} + } + + reason := Explicit + if pkg := h.dbExecutor.LocalPackage(aurPkg.Name); pkg != nil { + reason = Reason(pkg.Reason()) + + if h.needed { + if db.VerCmp(pkg.Version(), aurPkg.Version) >= 0 { + h.log.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(pkg.Name()+"-"+pkg.Version()))) + continue + } + } + } + + graph = h.GraphAURTarget(ctx, graph, aurPkg, &InstallInfo{ + AURBase: &aurPkg.PackageBase, + Reason: reason, + Source: AUR, + Version: aurPkg.Version, + }) + aurPkgsAdded = append(aurPkgsAdded, aurPkg) + } + + h.AddDepsForPkgs(ctx, aurPkgsAdded, graph) + + return graph, nil +} + +func (h *AURHandler) AddAurPkgProvides(pkg *aurc.Pkg, graph *topo.Graph[string, *InstallInfo]) { + for i := range pkg.Provides { + depName, mod, version := splitDep(pkg.Provides[i]) + h.log.Debugln(pkg.String() + " provides: " + depName) + graph.Provides(depName, &alpm.Depend{ + Name: depName, + Version: version, + Mod: aurDepModToAlpmDep(mod), + }, pkg.Name) + } +} + +// Removes found deps from the deps mapset and returns the found deps. +func (h *AURHandler) findDepsFromAUR(ctx context.Context, + deps mapset.Set[string], +) []aurc.Pkg { + pkgsToAdd := make([]aurc.Pkg, 0, deps.Cardinality()) + if deps.Cardinality() == 0 { + return []aurc.Pkg{} + } + + missingNeedles := make([]string, 0, deps.Cardinality()) + for _, depString := range deps.ToSlice() { + if _, ok := h.providerCache[depString]; !ok { + depName, _, _ := splitDep(depString) + missingNeedles = append(missingNeedles, depName) + } + } + + if len(missingNeedles) != 0 { + h.log.Debugln("deps to find", missingNeedles) + // provider search is more demanding than a simple search + // try to find name match if possible and then try to find provides. + aurPkgs, errCache := h.aurClient.Get(ctx, &aurc.Query{ + By: aurc.Name, Needles: missingNeedles, Contains: false, + }) + if errCache != nil { + h.log.Errorln(errCache) + } + + for i := range aurPkgs { + pkg := &aurPkgs[i] + if deps.Contains(pkg.Name) { + h.providerCache[pkg.Name] = append(h.providerCache[pkg.Name], *pkg) + } + + for _, val := range pkg.Provides { + if val == pkg.Name { + continue + } + if deps.Contains(val) { + h.providerCache[val] = append(h.providerCache[val], *pkg) + } + } + } + } + + for _, depString := range deps.ToSlice() { + var aurPkgs []aurc.Pkg + depName, _, _ := splitDep(depString) + + if cachedProvidePkg, ok := h.providerCache[depString]; ok { + aurPkgs = cachedProvidePkg + } else { + var errA error + aurPkgs, errA = h.aurClient.Get(ctx, &aurc.Query{By: aurc.Provides, Needles: []string{depName}, Contains: true}) + if errA != nil { + h.log.Errorln(gotext.Get("Failed to find AUR package for"), depString, ":", errA) + } + } + + // remove packages that don't satisfy the dependency + for i := 0; i < len(aurPkgs); i++ { + if !satisfiesAur(depString, &aurPkgs[i]) { + aurPkgs = append(aurPkgs[:i], aurPkgs[i+1:]...) + i-- + } + } + + if len(aurPkgs) == 0 { + h.log.Errorln(gotext.Get("No AUR package found for"), " ", depString) + + continue + } + + pkg := aurPkgs[0] + if len(aurPkgs) > 1 { + chosen := h.provideMenu(depString, aurPkgs) + pkg = *chosen + } + + h.providerCache[depString] = []aurc.Pkg{pkg} + deps.Remove(depString) + pkgsToAdd = append(pkgsToAdd, pkg) + } + + return pkgsToAdd +} + +func (h *AURHandler) addNodes( + ctx context.Context, + graph *topo.Graph[string, *InstallInfo], + parentPkgName string, + deps []string, + depType Reason, +) { + targetsToFind := mapset.NewThreadUnsafeSet(deps...) + // Check if in graph already + for _, depString := range targetsToFind.ToSlice() { + depName, _, _ := splitDep(depString) + if !graph.Exists(depName) && !graph.ProvidesExists(depName) { + continue + } + + if graph.Exists(depName) { + if err := graph.DependOn(depName, parentPkgName); err != nil { + h.log.Warnln(depString, parentPkgName, err) + } + + targetsToFind.Remove(depString) + } + + if p := graph.GetProviderNode(depName); p != nil { + if provideSatisfies(p.String(), depString, p.Version) { + if err := graph.DependOn(p.Provider, parentPkgName); err != nil { + h.log.Warnln(p.Provider, parentPkgName, err) + } + + targetsToFind.Remove(depString) + } + } + } + + // Check installed + for _, depString := range targetsToFind.ToSlice() { + depName, _, _ := splitDep(depString) + if !h.dbExecutor.LocalSatisfierExists(depString) { + continue + } + + if h.fullGraph { + validateAndSetNodeInfo( + graph, + depName, + &topo.NodeInfo[*InstallInfo]{Color: colorMap[depType], Background: bgColorMap[Local]}) + + if err := graph.DependOn(depName, parentPkgName); err != nil { + h.log.Warnln(depName, parentPkgName, err) + } + } + + targetsToFind.Remove(depString) + } + + // Check Sync + for _, depString := range targetsToFind.ToSlice() { + alpmPkg := h.dbExecutor.SyncSatisfier(depString) + if alpmPkg == nil { + continue + } + + if err := graph.DependOn(alpmPkg.Name(), parentPkgName); err != nil { + h.log.Warnln("repo dep warn:", depString, parentPkgName, err) + } + + dbName := alpmPkg.DB().Name() + validateAndSetNodeInfo( + graph, + alpmPkg.Name(), + &topo.NodeInfo[*InstallInfo]{ + Color: colorMap[depType], + Background: bgColorMap[Sync], + Value: &InstallInfo{ + Source: Sync, + Reason: depType, + Version: alpmPkg.Version(), + SyncDBName: &dbName, + }, + }) + + if newDeps := alpmPkg.Depends().Slice(); len(newDeps) != 0 && h.fullGraph { + newDepsSlice := make([]string, 0, len(newDeps)) + for _, newDep := range newDeps { + newDepsSlice = append(newDepsSlice, newDep.Name) + } + + h.addNodes(ctx, graph, alpmPkg.Name(), newDepsSlice, Dep) + } + + targetsToFind.Remove(depString) + } + + // Check AUR + pkgsToAdd := h.findDepsFromAUR(ctx, targetsToFind) + for i := range pkgsToAdd { + aurPkg := &pkgsToAdd[i] + if err := graph.DependOn(aurPkg.Name, parentPkgName); err != nil { + h.log.Warnln("aur dep warn:", aurPkg.Name, parentPkgName, err) + } + + graph.SetNodeInfo( + aurPkg.Name, + &topo.NodeInfo[*InstallInfo]{ + Color: colorMap[depType], + Background: bgColorMap[AUR], + Value: &InstallInfo{ + Source: AUR, + Reason: depType, + AURBase: &aurPkg.PackageBase, + Version: aurPkg.Version, + }, + }) + + h.addDepNodes(ctx, aurPkg, graph) + } + + // Add missing to graph + for _, depString := range targetsToFind.ToSlice() { + depName, mod, ver := splitDep(depString) + // no dep found. add as missing + if err := graph.DependOn(depName, parentPkgName); err != nil { + h.log.Warnln("missing dep warn:", depString, parentPkgName, err) + } + graph.SetNodeInfo(depName, &topo.NodeInfo[*InstallInfo]{ + Color: colorMap[depType], + Background: bgColorMap[Missing], + Value: &InstallInfo{ + Source: Missing, + Reason: depType, + Version: fmt.Sprintf("%s%s", mod, ver), + }, + }) + } +} + +func (h *AURHandler) provideMenu(dep string, options []aur.Pkg) *aur.Pkg { + size := len(options) + if size == 1 { + return &options[0] + } + + str := text.Bold(gotext.Get("There are %d providers available for %s:", size, dep)) + str += "\n" + + size = 1 + str += h.log.SprintOperationInfo(gotext.Get("Repository AUR"), "\n ") + + for i := range options { + str += fmt.Sprintf("%d) %s ", size, options[i].Name) + size++ + } + + h.log.OperationInfoln(str) + + for { + h.log.Println(gotext.Get("\nEnter a number (default=1): ")) + + if h.noConfirm { + h.log.Println("1") + + return &options[0] + } + + numberBuf, err := h.log.GetInput("", false) + if err != nil { + h.log.Errorln(err) + + break + } + + if numberBuf == "" { + return &options[0] + } + + num, err := strconv.Atoi(numberBuf) + if err != nil { + h.log.Errorln(gotext.Get("invalid number: %s", numberBuf)) + + continue + } + + if num < 1 || num >= size { + h.log.Errorln(gotext.Get("invalid value: %d is not between %d and %d", + num, 1, size-1)) + + continue + } + + return &options[num-1] + } + + return nil +} + +func aurDepModToAlpmDep(mod string) alpm.DepMod { + switch mod { + case "=": + return alpm.DepModEq + case ">=": + return alpm.DepModGE + case "<=": + return alpm.DepModLE + case ">": + return alpm.DepModGT + case "<": + return alpm.DepModLT + } + return alpm.DepModAny +} diff --git a/pkg/dep/aur_upgrade.go b/pkg/dep/aur_upgrade.go new file mode 100644 index 00000000..f43edbf6 --- /dev/null +++ b/pkg/dep/aur_upgrade.go @@ -0,0 +1,132 @@ +package dep + +import ( + "context" + + aurc "github.com/Jguer/aur" + "github.com/Jguer/go-alpm/v2" + "github.com/leonelquinteros/gotext" + + "github.com/Jguer/yay/v12/pkg/db" + "github.com/Jguer/yay/v12/pkg/dep/topo" + "github.com/Jguer/yay/v12/pkg/query" + aur "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/text" +) + +func (h *AURHandler) GraphUpgrades(ctx context.Context, graph *topo.Graph[string, *InstallInfo], + enableDowngrade bool, filter Filter, +) error { + h.log.OperationInfoln(gotext.Get("Searching AUR for updates...")) + if h.cfg.Devel { + h.log.OperationInfoln(gotext.Get("Checking development packages...")) + } + + aurdata := make(map[string]*aur.Pkg) + warnings := query.NewWarnings(h.log.Child("warnings")) + remote := h.dbExecutor.InstalledRemotePackages() + remoteNames := h.dbExecutor.InstalledRemotePackageNames() + + _aurdata, err := h.aurClient.Get(ctx, &aurc.Query{Needles: remoteNames, By: aurc.Name}) + if err != nil { + return err + } + + for i := range _aurdata { + pkg := &_aurdata[i] + aurdata[pkg.Name] = pkg + warnings.AddToWarnings(remote, pkg) + } + + h.upAUR(ctx, remote, aurdata, enableDowngrade, graph, filter) + + if h.cfg.Devel { + h.vcsStore.CleanOrphans(remote) + } + + warnings.CalculateMissing(remoteNames, remote, aurdata) + + return nil +} + +// UpAUR gathers foreign packages and checks if they have new versions. +// Output: Upgrade type package list. +func (h *AURHandler) upAUR(ctx context.Context, + remote map[string]db.IPackage, aurdata map[string]*query.Pkg, + enableDowngrade bool, graph *topo.Graph[string, *InstallInfo], + filter Filter, +) { + aurPkgsAdded := make([]*aurc.Pkg, 0) + for name, pkg := range remote { + aurPkg, ok := aurdata[name] + // Check for new versions + if ok && (db.VerCmp(pkg.Version(), aurPkg.Version) < 0) || + (enableDowngrade && (db.VerCmp(pkg.Version(), aurPkg.Version) > 0)) { + if pkg.ShouldIgnore() { + printIgnoringPackage(h.log, pkg, aurPkg.Version) + } else { + // check if deps are satisfied for aur packages + reason := Explicit + if pkg.Reason() == alpm.PkgReasonDepend { + reason = Dep + } + + // FIXME: Reimplement filter + graph = h.GraphAURTarget(ctx, graph, aurPkg, &InstallInfo{ + Reason: reason, + Source: AUR, + AURBase: &aurPkg.PackageBase, + Upgrade: true, + Version: aurPkg.Version, + LocalVersion: pkg.Version(), + }) + aurPkgsAdded = append(aurPkgsAdded, aurPkg) + continue + } + } + + if h.cfg.Devel { + if h.vcsStore.ToUpgrade(ctx, name) { + if _, ok := aurdata[name]; !ok { + h.log.Warnln(gotext.Get("ignoring package devel upgrade (no AUR info found):"), name) + continue + } + + if pkg.ShouldIgnore() { + printIgnoringPackage(h.log, pkg, "latest-commit") + continue + } + + // check if deps are satisfied for aur packages + reason := Explicit + if pkg.Reason() == alpm.PkgReasonDepend { + reason = Dep + } + + // FIXME: Reimplement filter + graph = h.GraphAURTarget(ctx, graph, aurPkg, &InstallInfo{ + Reason: reason, + Source: AUR, + AURBase: &aurPkg.PackageBase, + Upgrade: true, + Version: aurPkg.Version, + LocalVersion: pkg.Version(), + }) + aurPkgsAdded = append(aurPkgsAdded, aurPkg) + } + } + + } + + h.AddDepsForPkgs(ctx, aurPkgsAdded, graph) +} + +func printIgnoringPackage(log *text.Logger, pkg db.IPackage, newPkgVersion string) { + left, right := query.GetVersionDiff(pkg.Version(), newPkgVersion) + + pkgName := pkg.Name() + log.Warnln(gotext.Get("%s: ignoring package upgrade (%s => %s)", + text.Cyan(pkgName), + left, right, + )) +} diff --git a/pkg/dep/common.go b/pkg/dep/common.go new file mode 100644 index 00000000..f9a40d5a --- /dev/null +++ b/pkg/dep/common.go @@ -0,0 +1,20 @@ +package dep + +import "github.com/Jguer/yay/v12/pkg/dep/topo" + +func validateAndSetNodeInfo(graph *topo.Graph[string, *InstallInfo], + node string, nodeInfo *topo.NodeInfo[*InstallInfo], +) { + info := graph.GetNodeInfo(node) + if info != nil && info.Value != nil { + if info.Value.Reason < nodeInfo.Value.Reason { + return // refuse to downgrade reason + } + + if info.Value.Upgrade { + return // refuse to overwrite an upgrade + } + } + + graph.SetNodeInfo(node, nodeInfo) +} diff --git a/pkg/dep/dep_graph.go b/pkg/dep/dep_graph.go index 8f96265e..7047df10 100644 --- a/pkg/dep/dep_graph.go +++ b/pkg/dep/dep_graph.go @@ -3,20 +3,12 @@ package dep import ( "context" "fmt" - "strconv" - aurc "github.com/Jguer/aur" - alpm "github.com/Jguer/go-alpm/v2" - gosrc "github.com/Morganamilo/go-srcinfo" mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/dep/topo" - "github.com/Jguer/yay/v12/pkg/intrange" - aur "github.com/Jguer/yay/v12/pkg/query" - "github.com/Jguer/yay/v12/pkg/settings" - "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/text" ) @@ -105,65 +97,22 @@ type SourceHandler interface { Test(target Target) bool } -type Grapher struct { - logger *text.Logger - providerCache map[string][]aur.Pkg - cfg *settings.Configuration - - dbExecutor db.Executor - aurClient aurc.QueryClient - cmdBuilder exe.ICmdBuilder - fullGraph bool // If true, the graph will include all dependencies including already installed ones or repo - noConfirm bool // If true, the graph will not prompt for confirmation - noDeps bool // If true, the graph will not include dependencies - noCheckDeps bool // If true, the graph will not include check dependencies - needed bool // If true, the graph will only include packages that are not installed - handlers map[string][]SourceHandler +type UpgradeHandler interface { + GraphUpgrades(ctx context.Context, graph *topo.Graph[string, *InstallInfo], + enableDowngrade bool, filter Filter, + ) } -func NewGrapher(dbExecutor db.Executor, cfg *settings.Configuration, aurCache aurc.QueryClient, cmdBuilder exe.ICmdBuilder, - fullGraph, noConfirm, noDeps, noCheckDeps, needed bool, - logger *text.Logger, -) *Grapher { +type Grapher struct { + logger *text.Logger + handlers map[string][]SourceHandler + upgradeHandlers map[string][]UpgradeHandler +} + +func NewGrapher(logger *text.Logger) *Grapher { grapher := &Grapher{ - dbExecutor: dbExecutor, - cfg: cfg, - aurClient: aurCache, - cmdBuilder: cmdBuilder, - fullGraph: fullGraph, - noConfirm: noConfirm, - noDeps: noDeps, - noCheckDeps: noCheckDeps, - needed: needed, - providerCache: make(map[string][]aurc.Pkg, 5), - logger: logger, - handlers: make(map[string][]SourceHandler), - } - - grapher.RegisterSourceHandler(&AllSyncHandler{ - log: logger, - db: dbExecutor, - foundTargets: []alpm.IPackage{}, - }, "") - grapher.RegisterSourceHandler(&AllSyncGroupHandler{ - db: dbExecutor, - foundTargets: []Target{}, - }, "") - - grapher.RegisterSourceHandler(&SRCINFOHandler{ - log: logger, - db: dbExecutor, - cmdBuilder: cmdBuilder, - foundTargets: []string{}, - }, sourceCacheSRCINFO) - - for _, repo := range grapher.dbExecutor.Repos() { - grapher.RegisterSourceHandler(&SyncHandler{ - log: logger, - db: dbExecutor, - foundPkgs: []alpm.IPackage{}, - foundGroups: []Target{}, - }, repo) + logger: logger, + handlers: make(map[string][]SourceHandler), } return grapher @@ -175,6 +124,10 @@ func NewGraph() *topo.Graph[string, *InstallInfo] { func (g *Grapher) RegisterSourceHandler(handler SourceHandler, source string) { g.handlers[source] = append(g.handlers[source], handler) + + if upgradeHandler, ok := handler.(UpgradeHandler); ok { + g.upgradeHandlers[source] = append(g.upgradeHandlers[source], upgradeHandler) + } } func (g *Grapher) GraphFromTargets(ctx context.Context, @@ -184,8 +137,6 @@ func (g *Grapher) GraphFromTargets(ctx context.Context, graph = NewGraph() } - aurTargets := make([]string, 0, len(targets)) - sources := mapset.NewThreadUnsafeSetFromMapKeys[string, []SourceHandler](g.handlers) nextTarget: @@ -199,12 +150,6 @@ nextTarget: } g.logger.Errorln(gotext.Get("No package found for"), " ", target) - - switch target.DB { - case sourceAUR: - aurTargets = append(aurTargets, target.Name) - default: - } } for source := range sources.Iter() { @@ -215,548 +160,18 @@ nextTarget: } } - var errA error - graph, errA = g.GraphFromAUR(ctx, graph, aurTargets) - if errA != nil { - return nil, errA - } - return graph, nil } -func (g *Grapher) pickSrcInfoPkgs(pkgs []*aurc.Pkg) ([]*aurc.Pkg, error) { - final := make([]*aurc.Pkg, 0, len(pkgs)) - for i := range pkgs { - g.logger.Println(text.Magenta(strconv.Itoa(i+1)+" ") + text.Bold(pkgs[i].Name) + - " " + text.Cyan(pkgs[i].Version)) - g.logger.Println(" " + pkgs[i].Description) - } - g.logger.Infoln(gotext.Get("Packages to exclude") + " (eg: \"1 2 3\", \"1-3\", \"^4\"):") +// Filter decides if specific package should be included in theincluded in the results. +type Filter func(*db.Upgrade) bool - numberBuf, err := g.logger.GetInput("", g.noConfirm) - if err != nil { - return nil, err - } - - include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf) - isInclude := len(exclude) == 0 && otherExclude.Cardinality() == 0 - - for i := 1; i <= len(pkgs); i++ { - target := i - 1 - - if isInclude && !include.Get(i) { - final = append(final, pkgs[target]) - } - - if !isInclude && (exclude.Get(i)) { - final = append(final, pkgs[target]) - } - } - - return final, nil -} - -func addAurPkgProvides(logger *text.Logger, pkg *aurc.Pkg, graph *topo.Graph[string, *InstallInfo]) { - for i := range pkg.Provides { - depName, mod, version := splitDep(pkg.Provides[i]) - logger.Debugln(pkg.String() + " provides: " + depName) - graph.Provides(depName, &alpm.Depend{ - Name: depName, - Version: version, - Mod: aurDepModToAlpmDep(mod), - }, pkg.Name) - } -} - -func (g *Grapher) AddDepsForPkgs(ctx context.Context, pkgs []*aur.Pkg, graph *topo.Graph[string, *InstallInfo]) { - for _, pkg := range pkgs { - g.addDepNodes(ctx, pkg, graph) - } -} - -func (g *Grapher) addDepNodes(ctx context.Context, pkg *aur.Pkg, graph *topo.Graph[string, *InstallInfo]) { - if len(pkg.MakeDepends) > 0 { - g.addNodes(ctx, graph, pkg.Name, pkg.MakeDepends, MakeDep) - } - - if !g.noDeps && len(pkg.Depends) > 0 { - g.addNodes(ctx, graph, pkg.Name, pkg.Depends, Dep) - } - - if !g.noCheckDeps && !g.noDeps && len(pkg.CheckDepends) > 0 { - g.addNodes(ctx, graph, pkg.Name, pkg.CheckDepends, CheckDep) - } -} - -func (g *Grapher) GraphAURTarget(ctx context.Context, - graph *topo.Graph[string, *InstallInfo], - pkg *aurc.Pkg, instalInfo *InstallInfo, -) *topo.Graph[string, *InstallInfo] { - if graph == nil { - graph = NewGraph() - } - - graph.AddNode(pkg.Name) - - g.addAurPkgProvides(pkg, graph) - - validateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{ - Color: colorMap[instalInfo.Reason], - Background: bgColorMap[AUR], - Value: instalInfo, - }) - - return graph -} - -func (g *Grapher) GraphFromAUR(ctx context.Context, - graph *topo.Graph[string, *InstallInfo], - targets []string, +func (g *Grapher) GraphUpgrades(ctx context.Context, + graph *topo.Graph[string, *InstallInfo], enableDowngrade bool, ) (*topo.Graph[string, *InstallInfo], error) { if graph == nil { graph = NewGraph() } - if len(targets) == 0 { - return graph, nil - } - - aurPkgs, errCache := g.aurClient.Get(ctx, &aurc.Query{By: aurc.Name, Needles: targets}) - if errCache != nil { - g.logger.Errorln(errCache) - } - - for i := range aurPkgs { - pkg := &aurPkgs[i] - if _, ok := g.providerCache[pkg.Name]; !ok { - g.providerCache[pkg.Name] = []aurc.Pkg{*pkg} - } - } - - aurPkgsAdded := []*aurc.Pkg{} - - for _, target := range targets { - if cachedProvidePkg, ok := g.providerCache[target]; ok { - aurPkgs = cachedProvidePkg - } else { - var errA error - aurPkgs, errA = g.aurClient.Get(ctx, &aurc.Query{By: aurc.Provides, Needles: []string{target}, Contains: true}) - if errA != nil { - g.logger.Errorln(gotext.Get("Failed to find AUR package for"), " ", target, ":", errA) - } - } - - if len(aurPkgs) == 0 { - g.logger.Errorln(gotext.Get("No AUR package found for"), " ", target) - - continue - } - - aurPkg := &aurPkgs[0] - if len(aurPkgs) > 1 { - chosen := g.provideMenu(target, aurPkgs) - aurPkg = chosen - g.providerCache[target] = []aurc.Pkg{*aurPkg} - } - - reason := Explicit - if pkg := g.dbExecutor.LocalPackage(aurPkg.Name); pkg != nil { - reason = Reason(pkg.Reason()) - - if g.needed { - if db.VerCmp(pkg.Version(), aurPkg.Version) >= 0 { - g.logger.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(pkg.Name()+"-"+pkg.Version()))) - continue - } - } - } - - graph = g.GraphAURTarget(ctx, graph, aurPkg, &InstallInfo{ - AURBase: &aurPkg.PackageBase, - Reason: reason, - Source: AUR, - Version: aurPkg.Version, - }) - aurPkgsAdded = append(aurPkgsAdded, aurPkg) - } - - g.AddDepsForPkgs(ctx, aurPkgsAdded, graph) - return graph, nil } - -// Removes found deps from the deps mapset and returns the found deps. -func (g *Grapher) findDepsFromAUR(ctx context.Context, - deps mapset.Set[string], -) []aurc.Pkg { - pkgsToAdd := make([]aurc.Pkg, 0, deps.Cardinality()) - if deps.Cardinality() == 0 { - return []aurc.Pkg{} - } - - missingNeedles := make([]string, 0, deps.Cardinality()) - for _, depString := range deps.ToSlice() { - if _, ok := g.providerCache[depString]; !ok { - depName, _, _ := splitDep(depString) - missingNeedles = append(missingNeedles, depName) - } - } - - if len(missingNeedles) != 0 { - g.logger.Debugln("deps to find", missingNeedles) - // provider search is more demanding than a simple search - // try to find name match if possible and then try to find provides. - aurPkgs, errCache := g.aurClient.Get(ctx, &aurc.Query{ - By: aurc.Name, Needles: missingNeedles, Contains: false, - }) - if errCache != nil { - g.logger.Errorln(errCache) - } - - for i := range aurPkgs { - pkg := &aurPkgs[i] - if deps.Contains(pkg.Name) { - g.providerCache[pkg.Name] = append(g.providerCache[pkg.Name], *pkg) - } - - for _, val := range pkg.Provides { - if val == pkg.Name { - continue - } - if deps.Contains(val) { - g.providerCache[val] = append(g.providerCache[val], *pkg) - } - } - } - } - - for _, depString := range deps.ToSlice() { - var aurPkgs []aurc.Pkg - depName, _, _ := splitDep(depString) - - if cachedProvidePkg, ok := g.providerCache[depString]; ok { - aurPkgs = cachedProvidePkg - } else { - var errA error - aurPkgs, errA = g.aurClient.Get(ctx, &aurc.Query{By: aurc.Provides, Needles: []string{depName}, Contains: true}) - if errA != nil { - g.logger.Errorln(gotext.Get("Failed to find AUR package for"), depString, ":", errA) - } - } - - // remove packages that don't satisfy the dependency - for i := 0; i < len(aurPkgs); i++ { - if !satisfiesAur(depString, &aurPkgs[i]) { - aurPkgs = append(aurPkgs[:i], aurPkgs[i+1:]...) - i-- - } - } - - if len(aurPkgs) == 0 { - g.logger.Errorln(gotext.Get("No AUR package found for"), " ", depString) - - continue - } - - pkg := aurPkgs[0] - if len(aurPkgs) > 1 { - chosen := g.provideMenu(depString, aurPkgs) - pkg = *chosen - } - - g.providerCache[depString] = []aurc.Pkg{pkg} - deps.Remove(depString) - pkgsToAdd = append(pkgsToAdd, pkg) - } - - return pkgsToAdd -} - -func validateAndSetNodeInfo(graph *topo.Graph[string, *InstallInfo], - node string, nodeInfo *topo.NodeInfo[*InstallInfo], -) { - info := graph.GetNodeInfo(node) - if info != nil && info.Value != nil { - if info.Value.Reason < nodeInfo.Value.Reason { - return // refuse to downgrade reason - } - - if info.Value.Upgrade { - return // refuse to overwrite an upgrade - } - } - - graph.SetNodeInfo(node, nodeInfo) -} - -func (g *Grapher) addNodes( - ctx context.Context, - graph *topo.Graph[string, *InstallInfo], - parentPkgName string, - deps []string, - depType Reason, -) { - targetsToFind := mapset.NewThreadUnsafeSet(deps...) - // Check if in graph already - for _, depString := range targetsToFind.ToSlice() { - depName, _, _ := splitDep(depString) - if !graph.Exists(depName) && !graph.ProvidesExists(depName) { - continue - } - - if graph.Exists(depName) { - if err := graph.DependOn(depName, parentPkgName); err != nil { - g.logger.Warnln(depString, parentPkgName, err) - } - - targetsToFind.Remove(depString) - } - - if p := graph.GetProviderNode(depName); p != nil { - if provideSatisfies(p.String(), depString, p.Version) { - if err := graph.DependOn(p.Provider, parentPkgName); err != nil { - g.logger.Warnln(p.Provider, parentPkgName, err) - } - - targetsToFind.Remove(depString) - } - } - } - - // Check installed - for _, depString := range targetsToFind.ToSlice() { - depName, _, _ := splitDep(depString) - if !g.dbExecutor.LocalSatisfierExists(depString) { - continue - } - - if g.fullGraph { - validateAndSetNodeInfo( - graph, - depName, - &topo.NodeInfo[*InstallInfo]{Color: colorMap[depType], Background: bgColorMap[Local]}) - - if err := graph.DependOn(depName, parentPkgName); err != nil { - g.logger.Warnln(depName, parentPkgName, err) - } - } - - targetsToFind.Remove(depString) - } - - // Check Sync - for _, depString := range targetsToFind.ToSlice() { - alpmPkg := g.dbExecutor.SyncSatisfier(depString) - if alpmPkg == nil { - continue - } - - if err := graph.DependOn(alpmPkg.Name(), parentPkgName); err != nil { - g.logger.Warnln("repo dep warn:", depString, parentPkgName, err) - } - - dbName := alpmPkg.DB().Name() - validateAndSetNodeInfo( - graph, - alpmPkg.Name(), - &topo.NodeInfo[*InstallInfo]{ - Color: colorMap[depType], - Background: bgColorMap[Sync], - Value: &InstallInfo{ - Source: Sync, - Reason: depType, - Version: alpmPkg.Version(), - SyncDBName: &dbName, - }, - }) - - if newDeps := alpmPkg.Depends().Slice(); len(newDeps) != 0 && g.fullGraph { - newDepsSlice := make([]string, 0, len(newDeps)) - for _, newDep := range newDeps { - newDepsSlice = append(newDepsSlice, newDep.Name) - } - - g.addNodes(ctx, graph, alpmPkg.Name(), newDepsSlice, Dep) - } - - targetsToFind.Remove(depString) - } - - // Check AUR - pkgsToAdd := g.findDepsFromAUR(ctx, targetsToFind) - for i := range pkgsToAdd { - aurPkg := &pkgsToAdd[i] - if err := graph.DependOn(aurPkg.Name, parentPkgName); err != nil { - g.logger.Warnln("aur dep warn:", aurPkg.Name, parentPkgName, err) - } - - graph.SetNodeInfo( - aurPkg.Name, - &topo.NodeInfo[*InstallInfo]{ - Color: colorMap[depType], - Background: bgColorMap[AUR], - Value: &InstallInfo{ - Source: AUR, - Reason: depType, - AURBase: &aurPkg.PackageBase, - Version: aurPkg.Version, - }, - }) - - g.addDepNodes(ctx, aurPkg, graph) - } - - // Add missing to graph - for _, depString := range targetsToFind.ToSlice() { - depName, mod, ver := splitDep(depString) - // no dep found. add as missing - if err := graph.DependOn(depName, parentPkgName); err != nil { - g.logger.Warnln("missing dep warn:", depString, parentPkgName, err) - } - graph.SetNodeInfo(depName, &topo.NodeInfo[*InstallInfo]{ - Color: colorMap[depType], - Background: bgColorMap[Missing], - Value: &InstallInfo{ - Source: Missing, - Reason: depType, - Version: fmt.Sprintf("%s%s", mod, ver), - }, - }) - } -} - -func (g *Grapher) provideMenu(dep string, options []aur.Pkg) *aur.Pkg { - size := len(options) - if size == 1 { - return &options[0] - } - - str := text.Bold(gotext.Get("There are %d providers available for %s:", size, dep)) - str += "\n" - - size = 1 - str += g.logger.SprintOperationInfo(gotext.Get("Repository AUR"), "\n ") - - for i := range options { - str += fmt.Sprintf("%d) %s ", size, options[i].Name) - size++ - } - - g.logger.OperationInfoln(str) - - for { - g.logger.Println(gotext.Get("\nEnter a number (default=1): ")) - - if g.noConfirm { - g.logger.Println("1") - - return &options[0] - } - - numberBuf, err := g.logger.GetInput("", false) - if err != nil { - g.logger.Errorln(err) - - break - } - - if numberBuf == "" { - return &options[0] - } - - num, err := strconv.Atoi(numberBuf) - if err != nil { - g.logger.Errorln(gotext.Get("invalid number: %s", numberBuf)) - - continue - } - - if num < 1 || num >= size { - g.logger.Errorln(gotext.Get("invalid value: %d is not between %d and %d", - num, 1, size-1)) - - continue - } - - return &options[num-1] - } - - return nil -} - -func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*aur.Pkg, error) { - pkgs := make([]*aur.Pkg, 0, 1) - - alpmArch, err := dbExecutor.AlpmArchitectures() - if err != nil { - return nil, err - } - - alpmArch = append(alpmArch, "") // srcinfo assumes no value as "" - - getDesc := func(pkg *gosrc.Package) string { - if pkg.Pkgdesc != "" { - return pkg.Pkgdesc - } - - return srcInfo.Pkgdesc - } - - for i := range srcInfo.Packages { - pkg := &srcInfo.Packages[i] - - pkgs = append(pkgs, &aur.Pkg{ - ID: 0, - Name: pkg.Pkgname, - PackageBaseID: 0, - PackageBase: srcInfo.Pkgbase, - Version: srcInfo.Version(), - Description: getDesc(pkg), - URL: pkg.URL, - Depends: append(archStringToString(alpmArch, pkg.Depends), - archStringToString(alpmArch, srcInfo.Package.Depends)...), - MakeDepends: archStringToString(alpmArch, srcInfo.PackageBase.MakeDepends), - CheckDepends: archStringToString(alpmArch, srcInfo.PackageBase.CheckDepends), - Conflicts: append(archStringToString(alpmArch, pkg.Conflicts), - archStringToString(alpmArch, srcInfo.Package.Conflicts)...), - Provides: append(archStringToString(alpmArch, pkg.Provides), - archStringToString(alpmArch, srcInfo.Package.Provides)...), - Replaces: append(archStringToString(alpmArch, pkg.Replaces), - archStringToString(alpmArch, srcInfo.Package.Replaces)...), - OptDepends: []string{}, - Groups: pkg.Groups, - License: pkg.License, - Keywords: []string{}, - }) - } - - return pkgs, nil -} - -func archStringToString(alpmArches []string, archString []gosrc.ArchString) []string { - pkgs := make([]string, 0, len(archString)) - - for _, arch := range archString { - if db.ArchIsSupported(alpmArches, arch.Arch) { - pkgs = append(pkgs, arch.Value) - } - } - - return pkgs -} - -func aurDepModToAlpmDep(mod string) alpm.DepMod { - switch mod { - case "=": - return alpm.DepModEq - case ">=": - return alpm.DepModGE - case "<=": - return alpm.DepModLE - case ">": - return alpm.DepModGT - case "<": - return alpm.DepModLT - } - return alpm.DepModAny -} diff --git a/pkg/dep/srcinfo.go b/pkg/dep/srcinfo.go index b5e6bd91..7c36400c 100644 --- a/pkg/dep/srcinfo.go +++ b/pkg/dep/srcinfo.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "strconv" aurc "github.com/Jguer/aur" gosrc "github.com/Morganamilo/go-srcinfo" @@ -13,6 +14,8 @@ import ( "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/dep/topo" + "github.com/Jguer/yay/v12/pkg/intrange" + aur "github.com/Jguer/yay/v12/pkg/query" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/text" @@ -27,7 +30,10 @@ type SRCINFOHandler struct { log *text.Logger db db.Executor cmdBuilder exe.ICmdBuilder + noConfirm bool foundTargets []string + + aurHandler *AURHandler } func (g *SRCINFOHandler) Test(target Target) bool { @@ -93,7 +99,7 @@ func (g *SRCINFOHandler) GraphFromSrcInfoDirs(ctx context.Context, graph *topo.G graph.AddNode(pkg.Name) - addAurPkgProvides(g.log, pkg, graph) + g.aurHandler.AddAurPkgProvides(pkg, graph) validateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{ Color: colorMap[reason], @@ -111,7 +117,7 @@ func (g *SRCINFOHandler) GraphFromSrcInfoDirs(ctx context.Context, graph *topo.G aurPkgsAdded = append(aurPkgsAdded, aurPkgs...) } - g.AddDepsForPkgs(ctx, aurPkgsAdded, graph) + g.aurHandler.AddDepsForPkgs(ctx, aurPkgsAdded, graph) return graph, nil } @@ -143,3 +149,96 @@ func srcinfoExists(ctx context.Context, return fmt.Errorf("%w: %s", ErrNoBuildFiles, targetDir) } + +func (g *SRCINFOHandler) pickSrcInfoPkgs(pkgs []*aurc.Pkg) ([]*aurc.Pkg, error) { + final := make([]*aurc.Pkg, 0, len(pkgs)) + for i := range pkgs { + g.log.Println(text.Magenta(strconv.Itoa(i+1)+" ") + text.Bold(pkgs[i].Name) + + " " + text.Cyan(pkgs[i].Version)) + g.log.Println(" " + pkgs[i].Description) + } + g.log.Infoln(gotext.Get("Packages to exclude") + " (eg: \"1 2 3\", \"1-3\", \"^4\"):") + + numberBuf, err := g.log.GetInput("", g.noConfirm) + if err != nil { + return nil, err + } + + include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf) + isInclude := len(exclude) == 0 && otherExclude.Cardinality() == 0 + + for i := 1; i <= len(pkgs); i++ { + target := i - 1 + + if isInclude && !include.Get(i) { + final = append(final, pkgs[target]) + } + + if !isInclude && (exclude.Get(i)) { + final = append(final, pkgs[target]) + } + } + + return final, nil +} + +func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*aur.Pkg, error) { + pkgs := make([]*aur.Pkg, 0, 1) + + alpmArch, err := dbExecutor.AlpmArchitectures() + if err != nil { + return nil, err + } + + alpmArch = append(alpmArch, "") // srcinfo assumes no value as "" + + getDesc := func(pkg *gosrc.Package) string { + if pkg.Pkgdesc != "" { + return pkg.Pkgdesc + } + + return srcInfo.Pkgdesc + } + + for i := range srcInfo.Packages { + pkg := &srcInfo.Packages[i] + + pkgs = append(pkgs, &aur.Pkg{ + ID: 0, + Name: pkg.Pkgname, + PackageBaseID: 0, + PackageBase: srcInfo.Pkgbase, + Version: srcInfo.Version(), + Description: getDesc(pkg), + URL: pkg.URL, + Depends: append(archStringToString(alpmArch, pkg.Depends), + archStringToString(alpmArch, srcInfo.Package.Depends)...), + MakeDepends: archStringToString(alpmArch, srcInfo.PackageBase.MakeDepends), + CheckDepends: archStringToString(alpmArch, srcInfo.PackageBase.CheckDepends), + Conflicts: append(archStringToString(alpmArch, pkg.Conflicts), + archStringToString(alpmArch, srcInfo.Package.Conflicts)...), + Provides: append(archStringToString(alpmArch, pkg.Provides), + archStringToString(alpmArch, srcInfo.Package.Provides)...), + Replaces: append(archStringToString(alpmArch, pkg.Replaces), + archStringToString(alpmArch, srcInfo.Package.Replaces)...), + OptDepends: []string{}, + Groups: pkg.Groups, + License: pkg.License, + Keywords: []string{}, + }) + } + + return pkgs, nil +} + +func archStringToString(alpmArches []string, archString []gosrc.ArchString) []string { + pkgs := make([]string, 0, len(archString)) + + for _, arch := range archString { + if db.ArchIsSupported(alpmArches, arch.Arch) { + pkgs = append(pkgs, arch.Value) + } + } + + return pkgs +} diff --git a/pkg/dep/sync.go b/pkg/dep/sync.go index 57004385..3810f793 100644 --- a/pkg/dep/sync.go +++ b/pkg/dep/sync.go @@ -32,7 +32,7 @@ func (h *AllSyncHandler) Test(target Target) bool { func (h *AllSyncHandler) Graph(ctx context.Context, graph *topo.Graph[string, *InstallInfo]) error { for _, pkg := range h.foundTargets { - GraphSyncPkg(ctx, h.db, graph, h.log, pkg, nil) + graphSyncPkg(ctx, h.db, graph, h.log, pkg, nil) } return nil } @@ -72,7 +72,7 @@ func (h *SyncHandler) Test(target Target) bool { func (h *SyncHandler) Graph(ctx context.Context, graph *topo.Graph[string, *InstallInfo]) error { for _, pkg := range h.foundPkgs { - GraphSyncPkg(ctx, h.db, graph, h.log, pkg, nil) + graphSyncPkg(ctx, h.db, graph, h.log, pkg, nil) } for _, target := range h.foundGroups { @@ -104,7 +104,7 @@ func (h *AllSyncGroupHandler) Graph(ctx context.Context, graph *topo.Graph[strin return nil } -func GraphSyncPkg(ctx context.Context, dbExecutor db.Executor, +func graphSyncPkg(ctx context.Context, dbExecutor db.Executor, graph *topo.Graph[string, *InstallInfo], logger *text.Logger, pkg alpm.IPackage, upgradeInfo *db.SyncUpgrade, ) *topo.Graph[string, *InstallInfo] { diff --git a/pkg/dep/sync_upgrade.go b/pkg/dep/sync_upgrade.go new file mode 100644 index 00000000..26a805ed --- /dev/null +++ b/pkg/dep/sync_upgrade.go @@ -0,0 +1,39 @@ +package dep + +import ( + "context" + + "github.com/leonelquinteros/gotext" + + "github.com/Jguer/yay/v12/pkg/db" + "github.com/Jguer/yay/v12/pkg/dep/topo" +) + +func (h *AllSyncHandler) GraphUpgrades(ctx context.Context, graph *topo.Graph[string, *InstallInfo], + enableDowngrade bool, filter Filter, +) error { + h.log.OperationInfoln(gotext.Get("Searching databases for updates...")) + + syncUpgrades, err := h.db.SyncUpgrades(enableDowngrade) + if err != nil { + return err + } + + for _, up := range syncUpgrades { + if filter != nil && !filter(&db.Upgrade{ + Name: up.Package.Name(), + RemoteVersion: up.Package.Version(), + Repository: up.Package.DB().Name(), + Base: up.Package.Base(), + LocalVersion: up.LocalVersion, + Reason: up.Reason, + }) { + continue + } + + upgradeInfo := up + graph = graphSyncPkg(ctx, h.db, graph, h.log, up.Package, &upgradeInfo) + } + + return nil +} diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 499ee4f0..5c203d3d 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -9,6 +9,11 @@ import ( "github.com/leonelquinteros/gotext" + "github.com/Jguer/go-alpm/v2" + + "github.com/Jguer/yay/v12/pkg/db" + "github.com/Jguer/yay/v12/pkg/db/ialpm" + "github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/query" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" @@ -25,6 +30,7 @@ import ( type Runtime struct { Cfg *settings.Configuration + DB db.Executor QueryBuilder query.Builder PacmanConf *pacmanconf.Config VCSStore vcs.Store @@ -33,6 +39,7 @@ type Runtime struct { VoteClient *vote.Client AURClient aur.QueryClient Logger *text.Logger + Grapher *dep.Grapher } func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version string) (*Runtime, error) { @@ -91,6 +98,11 @@ func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version return nil, err } + dbExecutor, err := ialpm.NewExecutor(pacmanConf, logger.Child("db")) + if err != nil { + return nil, err + } + // FIXME: get rid of global text.UseColor = useColor @@ -110,8 +122,11 @@ func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version cfg.Mode, cfg.SearchBy, cfg.BottomUp, cfg.SingleLineResults, cfg.SeparateSources) + grapher := dep.NewGrapher(logger.Child("grapher")) + run := &Runtime{ Cfg: cfg, + DB: dbExecutor, QueryBuilder: queryBuilder, PacmanConf: pacmanConf, VCSStore: vcsStore, @@ -120,7 +135,44 @@ func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version VoteClient: voteClient, AURClient: aurCache, Logger: logger, + Grapher: grapher, } return run, nil } + +func RegisterHandlers(grapher *dep.Grapher) { + grapher.RegisterSourceHandler(&AllSyncHandler{ + log: logger, + db: dbExecutor, + foundTargets: []alpm.IPackage{}, + }, "") + grapher.RegisterSourceHandler(&AllSyncGroupHandler{ + db: dbExecutor, + foundTargets: []Target{}, + }, "") + + grapher.RegisterSourceHandler(&SRCINFOHandler{ + log: logger, + db: dbExecutor, + cmdBuilder: cmdBuilder, + foundTargets: []string{}, + }, sourceCacheSRCINFO) + + aurHandler := &AURHandler{ + log: logger, + db: dbExecutor, + } + grapher.RegisterSourceHandler(aurHandler, "") + + for _, repo := range grapher.dbExecutor.Repos() { + grapher.RegisterSourceHandler(&SyncHandler{ + log: logger, + db: dbExecutor, + foundPkgs: []alpm.IPackage{}, + foundGroups: []Target{}, + }, repo) + } + + grapher.RegisterSourceHandler(aurHandler, "aur") +} diff --git a/pkg/upgrade/service.go b/pkg/upgrade/service.go index 7bd7dea9..c43a8986 100644 --- a/pkg/upgrade/service.go +++ b/pkg/upgrade/service.go @@ -9,14 +9,12 @@ import ( "github.com/Jguer/aur" "github.com/Jguer/go-alpm/v2" - mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/dep/topo" "github.com/Jguer/yay/v12/pkg/intrange" - "github.com/Jguer/yay/v12/pkg/multierror" "github.com/Jguer/yay/v12/pkg/query" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/text" @@ -53,135 +51,6 @@ func NewUpgradeService(grapher *dep.Grapher, aurCache aur.QueryClient, } } -// upGraph adds packages to upgrade to the graph. -func (u *UpgradeService) upGraph(ctx context.Context, graph *topo.Graph[string, *dep.InstallInfo], - enableDowngrade bool, - filter Filter, -) (err error) { - var ( - develUp UpSlice - errs multierror.MultiError - aurdata = make(map[string]*aur.Pkg) - aurUp UpSlice - ) - - remote := u.dbExecutor.InstalledRemotePackages() - remoteNames := u.dbExecutor.InstalledRemotePackageNames() - - if u.cfg.Mode.AtLeastAUR() { - u.log.OperationInfoln(gotext.Get("Searching AUR for updates...")) - - _aurdata, err := u.aurCache.Get(ctx, &aur.Query{Needles: remoteNames, By: aur.Name}) - - errs.Add(err) - - if err == nil { - for i := range _aurdata { - pkg := &_aurdata[i] - aurdata[pkg.Name] = pkg - u.AURWarnings.AddToWarnings(remote, pkg) - } - - u.AURWarnings.CalculateMissing(remoteNames, remote, aurdata) - - aurUp = UpAUR(u.log, remote, aurdata, u.cfg.TimeUpdate, enableDowngrade) - - if u.cfg.Devel { - u.log.OperationInfoln(gotext.Get("Checking development packages...")) - - develUp = UpDevel(ctx, u.log, remote, aurdata, u.vcsStore) - - u.vcsStore.CleanOrphans(remote) - } - } - } - - aurPkgsAdded := []*aur.Pkg{} - - names := mapset.NewThreadUnsafeSet[string]() - for i := range develUp.Up { - up := &develUp.Up[i] - // check if deps are satisfied for aur packages - reason := dep.Explicit - if up.Reason == alpm.PkgReasonDepend { - reason = dep.Dep - } - - if filter != nil && !filter(up) { - continue - } - - aurPkg := aurdata[up.Name] - graph = u.grapher.GraphAURTarget(ctx, graph, aurPkg, &dep.InstallInfo{ - Reason: reason, - Source: dep.AUR, - AURBase: &aurPkg.PackageBase, - Upgrade: true, - Devel: true, - LocalVersion: up.LocalVersion, - Version: up.RemoteVersion, - }) - names.Add(up.Name) - aurPkgsAdded = append(aurPkgsAdded, aurPkg) - } - - for i := range aurUp.Up { - up := &aurUp.Up[i] - // add devel packages if they are not already in the list - if names.Contains(up.Name) { - continue - } - - // check if deps are satisfied for aur packages - reason := dep.Explicit - if up.Reason == alpm.PkgReasonDepend { - reason = dep.Dep - } - - if filter != nil && !filter(up) { - continue - } - - aurPkg := aurdata[up.Name] - graph = u.grapher.GraphAURTarget(ctx, graph, aurPkg, &dep.InstallInfo{ - Reason: reason, - Source: dep.AUR, - AURBase: &aurPkg.PackageBase, - Upgrade: true, - Version: up.RemoteVersion, - LocalVersion: up.LocalVersion, - }) - aurPkgsAdded = append(aurPkgsAdded, aurPkg) - } - - u.grapher.AddDepsForPkgs(ctx, aurPkgsAdded, graph) - - if u.cfg.Mode.AtLeastRepo() { - u.log.OperationInfoln(gotext.Get("Searching databases for updates...")) - - syncUpgrades, err := u.dbExecutor.SyncUpgrades(enableDowngrade) - for _, up := range syncUpgrades { - if filter != nil && !filter(&db.Upgrade{ - Name: up.Package.Name(), - RemoteVersion: up.Package.Version(), - Repository: up.Package.DB().Name(), - Base: up.Package.Base(), - LocalVersion: up.LocalVersion, - Reason: up.Reason, - }) { - continue - } - - upgradeInfo := up - graph = dep.GraphSyncPkg(ctx, u.dbExecutor, graph, u.log, up.Package, &upgradeInfo) - } - - errs.Add(err) - } - - return errs.Return() -} - func (u *UpgradeService) graphToUpSlice(graph *topo.Graph[string, *dep.InstallInfo]) (aurUp, repoUp UpSlice) { aurUp = UpSlice{Up: make([]Upgrade, 0, graph.Len())} repoUp = UpSlice{Up: make([]Upgrade, 0, graph.Len()), Repos: u.dbExecutor.Repos()} @@ -237,19 +106,11 @@ func (u *UpgradeService) GraphUpgrades(ctx context.Context, graph *topo.Graph[string, *dep.InstallInfo], enableDowngrade bool, filter Filter, ) (*topo.Graph[string, *dep.InstallInfo], error) { - if graph == nil { - graph = dep.NewGraph() - } - - err := u.upGraph(ctx, graph, enableDowngrade, filter) + graph, err := u.grapher.GraphUpgrades(ctx, graph, enableDowngrade) if err != nil { return graph, err } - if graph.Len() == 0 { - return graph, nil - } - return graph, nil } diff --git a/pkg/upgrade/sources.go b/pkg/upgrade/sources.go index e8b31d4e..d1d1ab34 100644 --- a/pkg/upgrade/sources.go +++ b/pkg/upgrade/sources.go @@ -18,7 +18,6 @@ func UpDevel( aurdata map[string]*query.Pkg, localCache vcs.Store, ) UpSlice { - toRemove := make([]string, 0) toUpgrade := UpSlice{Up: make([]Upgrade, 0), Repos: []string{"devel"}} for pkgName, pkg := range remote { @@ -45,8 +44,6 @@ func UpDevel( } } - localCache.RemovePackages(toRemove) - return toUpgrade }