service.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package upgrade
  2. import (
  3. "context"
  4. "sort"
  5. "github.com/Jguer/aur"
  6. "github.com/Jguer/aur/metadata"
  7. "github.com/Jguer/go-alpm/v2"
  8. mapset "github.com/deckarep/golang-set/v2"
  9. "github.com/leonelquinteros/gotext"
  10. "github.com/Jguer/yay/v11/pkg/db"
  11. "github.com/Jguer/yay/v11/pkg/dep"
  12. "github.com/Jguer/yay/v11/pkg/intrange"
  13. "github.com/Jguer/yay/v11/pkg/multierror"
  14. "github.com/Jguer/yay/v11/pkg/query"
  15. "github.com/Jguer/yay/v11/pkg/settings"
  16. "github.com/Jguer/yay/v11/pkg/text"
  17. "github.com/Jguer/yay/v11/pkg/topo"
  18. "github.com/Jguer/yay/v11/pkg/vcs"
  19. )
  20. type UpgradeService struct {
  21. grapher *dep.Grapher
  22. aurCache settings.AURCache
  23. aurClient aur.ClientInterface
  24. dbExecutor db.Executor
  25. vcsStore vcs.Store
  26. runtime *settings.Runtime
  27. cfg *settings.Configuration
  28. log *text.Logger
  29. noConfirm bool
  30. }
  31. func NewUpgradeService(grapher *dep.Grapher, aurCache settings.AURCache,
  32. aurClient aur.ClientInterface, dbExecutor db.Executor,
  33. vcsStore vcs.Store, runtime *settings.Runtime, cfg *settings.Configuration,
  34. noConfirm bool, logger *text.Logger,
  35. ) *UpgradeService {
  36. return &UpgradeService{
  37. grapher: grapher,
  38. aurCache: aurCache,
  39. aurClient: aurClient,
  40. dbExecutor: dbExecutor,
  41. vcsStore: vcsStore,
  42. runtime: runtime,
  43. cfg: cfg,
  44. noConfirm: noConfirm,
  45. log: logger,
  46. }
  47. }
  48. // upGraph adds packages to upgrade to the graph.
  49. func (u *UpgradeService) upGraph(ctx context.Context, graph *topo.Graph[string, *dep.InstallInfo],
  50. warnings *query.AURWarnings, enableDowngrade bool,
  51. filter Filter,
  52. ) (err error) {
  53. var (
  54. develUp UpSlice
  55. errs multierror.MultiError
  56. aurdata = make(map[string]*aur.Pkg)
  57. aurUp UpSlice
  58. )
  59. remote := u.dbExecutor.InstalledRemotePackages()
  60. remoteNames := u.dbExecutor.InstalledRemotePackageNames()
  61. if u.runtime.Mode.AtLeastAUR() {
  62. u.log.OperationInfoln(gotext.Get("Searching AUR for updates..."))
  63. var _aurdata []aur.Pkg
  64. if u.aurCache != nil {
  65. _aurdata, err = u.aurCache.Get(ctx, &metadata.AURQuery{Needles: remoteNames, By: aur.Name})
  66. } else {
  67. _aurdata, err = query.AURInfo(ctx, u.aurClient, remoteNames, warnings, u.cfg.RequestSplitN)
  68. }
  69. errs.Add(err)
  70. if err == nil {
  71. for i := range _aurdata {
  72. pkg := &_aurdata[i]
  73. aurdata[pkg.Name] = pkg
  74. }
  75. aurUp = UpAUR(remote, aurdata, u.cfg.TimeUpdate)
  76. }
  77. if u.cfg.Devel {
  78. u.log.OperationInfoln(gotext.Get("Checking development packages..."))
  79. develUp = UpDevel(ctx, remote, aurdata, u.vcsStore)
  80. u.vcsStore.CleanOrphans(remote)
  81. }
  82. }
  83. names := mapset.NewThreadUnsafeSet[string]()
  84. for i := range develUp.Up {
  85. up := &develUp.Up[i]
  86. // check if deps are satisfied for aur packages
  87. reason := dep.Explicit
  88. if up.Reason == alpm.PkgReasonDepend {
  89. reason = dep.Dep
  90. }
  91. if filter != nil && !filter(up) {
  92. continue
  93. }
  94. aurPkg := aurdata[up.Name]
  95. graph = u.grapher.GraphAURTarget(ctx, graph, aurPkg, &dep.InstallInfo{
  96. Reason: reason,
  97. Source: dep.AUR,
  98. AURBase: &aurPkg.PackageBase,
  99. Upgrade: true,
  100. Devel: true,
  101. LocalVersion: up.LocalVersion,
  102. })
  103. names.Add(up.Name)
  104. }
  105. for i := range aurUp.Up {
  106. up := &aurUp.Up[i]
  107. // add devel packages if they are not already in the list
  108. if names.Contains(up.Name) {
  109. continue
  110. }
  111. // check if deps are satisfied for aur packages
  112. reason := dep.Explicit
  113. if up.Reason == alpm.PkgReasonDepend {
  114. reason = dep.Dep
  115. }
  116. if filter != nil && !filter(up) {
  117. continue
  118. }
  119. aurPkg := aurdata[up.Name]
  120. graph = u.grapher.GraphAURTarget(ctx, graph, aurPkg, &dep.InstallInfo{
  121. Reason: reason,
  122. Source: dep.AUR,
  123. AURBase: &aurPkg.PackageBase,
  124. Upgrade: true,
  125. Version: up.RemoteVersion,
  126. LocalVersion: up.LocalVersion,
  127. })
  128. }
  129. if u.cfg.Runtime.Mode.AtLeastRepo() {
  130. u.log.OperationInfoln(gotext.Get("Searching databases for updates..."))
  131. syncUpgrades, err := u.dbExecutor.SyncUpgrades(enableDowngrade)
  132. for _, up := range syncUpgrades {
  133. dbName := up.Package.DB().Name()
  134. if filter != nil && !filter(&db.Upgrade{
  135. Name: up.Package.Name(),
  136. RemoteVersion: up.Package.Version(),
  137. Repository: dbName,
  138. Base: up.Package.Base(),
  139. LocalVersion: up.LocalVersion,
  140. Reason: up.Reason,
  141. }) {
  142. continue
  143. }
  144. reason := dep.Explicit
  145. if up.Reason == alpm.PkgReasonDepend {
  146. reason = dep.Dep
  147. }
  148. graph = u.grapher.GraphSyncPkg(ctx, graph, up.Package, &dep.InstallInfo{
  149. Source: dep.Sync,
  150. Reason: reason,
  151. Version: up.Package.Version(),
  152. SyncDBName: &dbName,
  153. LocalVersion: up.LocalVersion,
  154. Upgrade: true,
  155. })
  156. }
  157. errs.Add(err)
  158. }
  159. return errs.Return()
  160. }
  161. func (u *UpgradeService) graphToUpSlice(graph *topo.Graph[string, *dep.InstallInfo]) (aurUp, repoUp UpSlice) {
  162. aurUp = UpSlice{Up: make([]Upgrade, 0, graph.Len())}
  163. repoUp = UpSlice{Up: make([]Upgrade, 0, graph.Len()), Repos: u.dbExecutor.Repos()}
  164. _ = graph.ForEach(func(name string, info *dep.InstallInfo) error {
  165. alpmReason := alpm.PkgReasonExplicit
  166. if info.Reason == dep.Dep {
  167. alpmReason = alpm.PkgReasonDepend
  168. }
  169. if info.Source == dep.AUR {
  170. aurRepo := "aur"
  171. if info.Devel {
  172. aurRepo = "devel"
  173. }
  174. aurUp.Up = append(aurUp.Up, Upgrade{
  175. Name: name,
  176. RemoteVersion: info.Version,
  177. Repository: aurRepo,
  178. Base: *info.AURBase,
  179. LocalVersion: info.LocalVersion,
  180. Reason: alpmReason,
  181. })
  182. } else if info.Source == dep.Sync {
  183. repoUp.Up = append(repoUp.Up, Upgrade{
  184. Name: name,
  185. RemoteVersion: info.Version,
  186. Repository: *info.SyncDBName,
  187. Base: "",
  188. LocalVersion: info.LocalVersion,
  189. Reason: alpmReason,
  190. })
  191. }
  192. return nil
  193. })
  194. return aurUp, repoUp
  195. }
  196. func (u *UpgradeService) GraphUpgrades(ctx context.Context,
  197. graph *topo.Graph[string, *dep.InstallInfo],
  198. enableDowngrade bool,
  199. ) (*topo.Graph[string, *dep.InstallInfo], error) {
  200. if graph == nil {
  201. graph = topo.New[string, *dep.InstallInfo]()
  202. }
  203. warnings := query.NewWarnings()
  204. err := u.upGraph(ctx, graph, warnings, enableDowngrade,
  205. func(*Upgrade) bool { return true })
  206. if err != nil {
  207. return graph, err
  208. }
  209. warnings.Print()
  210. if graph.Len() == 0 {
  211. return graph, nil
  212. }
  213. errUp := u.userExcludeUpgrades(graph)
  214. return graph, errUp
  215. }
  216. // userExcludeUpgrades asks the user which packages to exclude from the upgrade and
  217. // removes them from the graph
  218. func (u *UpgradeService) userExcludeUpgrades(graph *topo.Graph[string, *dep.InstallInfo]) error {
  219. allUpLen := graph.Len()
  220. aurUp, repoUp := u.graphToUpSlice(graph)
  221. sort.Sort(repoUp)
  222. sort.Sort(aurUp)
  223. allUp := UpSlice{Up: append(repoUp.Up, aurUp.Up...), Repos: append(repoUp.Repos, aurUp.Repos...)}
  224. u.log.Printf("%s"+text.Bold(" %d ")+"%s\n", text.Bold(text.Cyan("::")), allUpLen, text.Bold(gotext.Get("Packages to upgrade.")))
  225. allUp.Print(u.log)
  226. u.log.Infoln(gotext.Get("Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"))
  227. u.log.Warnln(gotext.Get("May cause partial upgrades and break systems"))
  228. numbers, err := u.log.GetInput(u.cfg.AnswerUpgrade, settings.NoConfirm)
  229. if err != nil {
  230. return err
  231. }
  232. // upgrade menu asks you which packages to NOT upgrade so in this case
  233. // exclude and include are kind of swapped
  234. exclude, include, otherExclude, otherInclude := intrange.ParseNumberMenu(numbers)
  235. isInclude := len(include) == 0 && len(otherInclude) == 0
  236. for i := range allUp.Up {
  237. up := &allUp.Up[i]
  238. if isInclude && otherExclude.Get(up.Repository) {
  239. u.log.Debugln("pruning", up.Name)
  240. graph.Prune(up.Name)
  241. }
  242. if isInclude && exclude.Get(allUpLen-i) {
  243. u.log.Debugln("pruning", up.Name)
  244. graph.Prune(up.Name)
  245. continue
  246. }
  247. if !isInclude && !(include.Get(allUpLen-i) || otherInclude.Get(up.Repository)) {
  248. u.log.Debugln("pruning", up.Name)
  249. graph.Prune(up.Name)
  250. continue
  251. }
  252. }
  253. return nil
  254. }