service.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package upgrade
  2. import (
  3. "context"
  4. "fmt"
  5. "math"
  6. "sort"
  7. "strings"
  8. "github.com/Jguer/aur"
  9. "github.com/Jguer/go-alpm/v2"
  10. "github.com/leonelquinteros/gotext"
  11. "github.com/Jguer/yay/v12/pkg/db"
  12. "github.com/Jguer/yay/v12/pkg/dep"
  13. "github.com/Jguer/yay/v12/pkg/dep/topo"
  14. "github.com/Jguer/yay/v12/pkg/intrange"
  15. "github.com/Jguer/yay/v12/pkg/query"
  16. "github.com/Jguer/yay/v12/pkg/settings"
  17. "github.com/Jguer/yay/v12/pkg/text"
  18. "github.com/Jguer/yay/v12/pkg/vcs"
  19. )
  20. const cutOffExtra = 2
  21. type UpgradeService struct {
  22. grapher *dep.Grapher
  23. aurCache aur.QueryClient
  24. dbExecutor db.Executor
  25. vcsStore vcs.Store
  26. cfg *settings.Configuration
  27. log *text.Logger
  28. noConfirm bool
  29. AURWarnings *query.AURWarnings
  30. }
  31. func NewUpgradeService(grapher *dep.Grapher, aurCache aur.QueryClient,
  32. dbExecutor db.Executor, vcsStore vcs.Store,
  33. cfg *settings.Configuration, noConfirm bool, logger *text.Logger,
  34. ) *UpgradeService {
  35. return &UpgradeService{
  36. grapher: grapher,
  37. aurCache: aurCache,
  38. dbExecutor: dbExecutor,
  39. vcsStore: vcsStore,
  40. cfg: cfg,
  41. noConfirm: noConfirm,
  42. log: logger,
  43. AURWarnings: query.NewWarnings(logger.Child("warnings")),
  44. }
  45. }
  46. func (u *UpgradeService) graphToUpSlice(graph *topo.Graph[string, *dep.InstallInfo]) (aurUp, repoUp UpSlice) {
  47. aurUp = UpSlice{Up: make([]Upgrade, 0, graph.Len())}
  48. repoUp = UpSlice{Up: make([]Upgrade, 0, graph.Len()), Repos: u.dbExecutor.Repos()}
  49. _ = graph.ForEach(func(name string, info *dep.InstallInfo) error {
  50. alpmReason := alpm.PkgReasonDepend
  51. if info.Reason == dep.Explicit {
  52. alpmReason = alpm.PkgReasonExplicit
  53. }
  54. parents := graph.ImmediateDependencies(name)
  55. extra := ""
  56. if len(parents) > 0 && !info.Upgrade && info.Reason == dep.MakeDep {
  57. reducedParents := parents.Slice()[:int(math.Min(cutOffExtra, float64(len(parents))))]
  58. if len(parents) > cutOffExtra {
  59. reducedParents = append(reducedParents, "...")
  60. }
  61. extra = fmt.Sprintf(" (%s of %s)", dep.ReasonNames[info.Reason], strings.Join(reducedParents, ", "))
  62. }
  63. if info.Source == dep.AUR {
  64. aurRepo := "aur"
  65. if info.Devel {
  66. aurRepo = "devel"
  67. }
  68. aurUp.Up = append(aurUp.Up, Upgrade{
  69. Name: name,
  70. RemoteVersion: info.Version,
  71. Repository: aurRepo,
  72. Base: *info.AURBase,
  73. LocalVersion: info.LocalVersion,
  74. Reason: alpmReason,
  75. Extra: extra,
  76. })
  77. } else if info.Source == dep.Sync {
  78. repoUp.Up = append(repoUp.Up, Upgrade{
  79. Name: name,
  80. RemoteVersion: info.Version,
  81. Repository: *info.SyncDBName,
  82. Base: "",
  83. LocalVersion: info.LocalVersion,
  84. Reason: alpmReason,
  85. Extra: extra,
  86. })
  87. }
  88. return nil
  89. })
  90. return aurUp, repoUp
  91. }
  92. func (u *UpgradeService) GraphUpgrades(ctx context.Context,
  93. graph *topo.Graph[string, *dep.InstallInfo],
  94. enableDowngrade bool, filter Filter,
  95. ) (*topo.Graph[string, *dep.InstallInfo], error) {
  96. graph, err := u.grapher.GraphUpgrades(ctx, graph, enableDowngrade)
  97. if err != nil {
  98. return graph, err
  99. }
  100. return graph, nil
  101. }
  102. // userExcludeUpgrades asks the user which packages to exclude from the upgrade and
  103. // removes them from the graph
  104. func (u *UpgradeService) UserExcludeUpgrades(graph *topo.Graph[string, *dep.InstallInfo]) ([]string, error) {
  105. if graph.Len() == 0 {
  106. return []string{}, nil
  107. }
  108. aurUp, repoUp := u.graphToUpSlice(graph)
  109. sort.Sort(repoUp)
  110. sort.Sort(aurUp)
  111. allUp := UpSlice{Repos: append(repoUp.Repos, aurUp.Repos...)}
  112. for _, up := range repoUp.Up {
  113. if up.LocalVersion == "" && up.Reason != alpm.PkgReasonExplicit {
  114. allUp.PulledDeps = append(allUp.PulledDeps, up)
  115. } else {
  116. allUp.Up = append(allUp.Up, up)
  117. }
  118. }
  119. for _, up := range aurUp.Up {
  120. if up.LocalVersion == "" && up.Reason != alpm.PkgReasonExplicit {
  121. allUp.PulledDeps = append(allUp.PulledDeps, up)
  122. } else {
  123. allUp.Up = append(allUp.Up, up)
  124. }
  125. }
  126. if len(allUp.PulledDeps) > 0 {
  127. u.log.Printf("%s"+text.Bold(" %d ")+"%s\n", text.Bold(text.Cyan("::")),
  128. len(allUp.PulledDeps), text.Bold(gotext.Get("%s will also be installed for this operation.",
  129. gotext.GetN("dependency", "dependencies", len(allUp.PulledDeps)))))
  130. allUp.PrintDeps(u.log)
  131. }
  132. u.log.Printf("%s"+text.Bold(" %d ")+"%s\n", text.Bold(text.Cyan("::")),
  133. len(allUp.Up), text.Bold(gotext.Get("%s to upgrade/install.", gotext.GetN("package", "packages", len(allUp.Up)))))
  134. allUp.Print(u.log)
  135. u.log.Infoln(gotext.Get("Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"))
  136. u.log.Warnln(gotext.Get("Excluding packages may cause partial upgrades and break systems"))
  137. numbers, err := u.log.GetInput(u.cfg.AnswerUpgrade, settings.NoConfirm)
  138. if err != nil {
  139. return nil, err
  140. }
  141. // upgrade menu asks you which packages to NOT upgrade so in this case
  142. // exclude and include are kind of swapped
  143. exclude, include, otherExclude, otherInclude := intrange.ParseNumberMenu(numbers)
  144. isInclude := len(include) == 0 && otherInclude.Cardinality() == 0
  145. excluded := make([]string, 0)
  146. for i := range allUp.Up {
  147. up := &allUp.Up[i]
  148. if isInclude && otherExclude.Contains(up.Repository) {
  149. u.log.Debugln("pruning", up.Name)
  150. excluded = append(excluded, graph.Prune(up.Name)...)
  151. continue
  152. }
  153. if isInclude && exclude.Get(len(allUp.Up)-i) {
  154. u.log.Debugln("pruning", up.Name)
  155. excluded = append(excluded, graph.Prune(up.Name)...)
  156. continue
  157. }
  158. if !isInclude && !(include.Get(len(allUp.Up)-i) || otherInclude.Contains(up.Repository)) {
  159. u.log.Debugln("pruning", up.Name)
  160. excluded = append(excluded, graph.Prune(up.Name)...)
  161. continue
  162. }
  163. }
  164. return excluded, nil
  165. }