upgrade.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package main
  2. import (
  3. "fmt"
  4. "sort"
  5. "sync"
  6. "unicode"
  7. alpm "github.com/Jguer/go-alpm"
  8. "github.com/Jguer/yay/v9/pkg/types"
  9. rpc "github.com/mikkeloscar/aur"
  10. )
  11. // upgrade type describes a system upgrade.
  12. type upgrade struct {
  13. Name string
  14. Repository string
  15. LocalVersion string
  16. RemoteVersion string
  17. }
  18. // upSlice is a slice of Upgrades
  19. type upSlice []upgrade
  20. func (u upSlice) Len() int { return len(u) }
  21. func (u upSlice) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
  22. func (u upSlice) Less(i, j int) bool {
  23. if u[i].Repository == u[j].Repository {
  24. iRunes := []rune(u[i].Name)
  25. jRunes := []rune(u[j].Name)
  26. return types.LessRunes(iRunes, jRunes)
  27. }
  28. syncDB, err := alpmHandle.SyncDBs()
  29. if err != nil {
  30. iRunes := []rune(u[i].Repository)
  31. jRunes := []rune(u[j].Repository)
  32. return types.LessRunes(iRunes, jRunes)
  33. }
  34. less := false
  35. found := syncDB.ForEach(func(db alpm.DB) error {
  36. switch db.Name() {
  37. case u[i].Repository:
  38. less = true
  39. case u[j].Repository:
  40. less = false
  41. default:
  42. return nil
  43. }
  44. return fmt.Errorf("")
  45. })
  46. if found != nil {
  47. return less
  48. }
  49. iRunes := []rune(u[i].Repository)
  50. jRunes := []rune(u[j].Repository)
  51. return types.LessRunes(iRunes, jRunes)
  52. }
  53. func getVersionDiff(oldVersion, newVersion string) (left, right string) {
  54. if oldVersion == newVersion {
  55. return oldVersion + red(""), newVersion + green("")
  56. }
  57. diffPosition := 0
  58. checkWords := func(str string, index int, words ...string) bool {
  59. for _, word := range words {
  60. wordLength := len(word)
  61. nextIndex := index + 1
  62. if (index < len(str)-wordLength) &&
  63. (str[nextIndex:(nextIndex+wordLength)] == word) {
  64. return true
  65. }
  66. }
  67. return false
  68. }
  69. for index, char := range oldVersion {
  70. charIsSpecial := !(unicode.IsLetter(char) || unicode.IsNumber(char))
  71. if (index >= len(newVersion)) || (char != rune(newVersion[index])) {
  72. if charIsSpecial {
  73. diffPosition = index
  74. }
  75. break
  76. }
  77. if charIsSpecial ||
  78. (((index == len(oldVersion)-1) || (index == len(newVersion)-1)) &&
  79. ((len(oldVersion) != len(newVersion)) ||
  80. (oldVersion[index] == newVersion[index]))) ||
  81. checkWords(oldVersion, index, "rc", "pre", "alpha", "beta") {
  82. diffPosition = index + 1
  83. }
  84. }
  85. samePart := oldVersion[0:diffPosition]
  86. left = samePart + red(oldVersion[diffPosition:])
  87. right = samePart + green(newVersion[diffPosition:])
  88. return
  89. }
  90. // upList returns lists of packages to upgrade from each source.
  91. func upList(warnings *aurWarnings) (upSlice, upSlice, error) {
  92. local, remote, _, remoteNames, err := filterPackages()
  93. if err != nil {
  94. return nil, nil, err
  95. }
  96. var wg sync.WaitGroup
  97. var develUp upSlice
  98. var repoUp upSlice
  99. var aurUp upSlice
  100. var errs types.MultiError
  101. aurdata := make(map[string]*rpc.Pkg)
  102. if mode == modeAny || mode == modeRepo {
  103. fmt.Println(bold(cyan("::") + bold(" Searching databases for updates...")))
  104. wg.Add(1)
  105. go func() {
  106. repoUp, err = upRepo(local)
  107. errs.Add(err)
  108. wg.Done()
  109. }()
  110. }
  111. if mode == modeAny || mode == modeAUR {
  112. fmt.Println(bold(cyan("::") + bold(" Searching AUR for updates...")))
  113. var _aurdata []*rpc.Pkg
  114. _aurdata, err = aurInfo(remoteNames, warnings)
  115. errs.Add(err)
  116. if err == nil {
  117. for _, pkg := range _aurdata {
  118. aurdata[pkg.Name] = pkg
  119. }
  120. wg.Add(1)
  121. go func() {
  122. aurUp, err = upAUR(remote, aurdata)
  123. errs.Add(err)
  124. wg.Done()
  125. }()
  126. if config.Devel {
  127. fmt.Println(bold(cyan("::") + bold(" Checking development packages...")))
  128. wg.Add(1)
  129. go func() {
  130. develUp = upDevel(remote, aurdata)
  131. wg.Done()
  132. }()
  133. }
  134. }
  135. }
  136. wg.Wait()
  137. printLocalNewerThanAUR(remote, aurdata)
  138. if develUp != nil {
  139. names := make(types.StringSet)
  140. for _, up := range develUp {
  141. names.Set(up.Name)
  142. }
  143. for _, up := range aurUp {
  144. if !names.Get(up.Name) {
  145. develUp = append(develUp, up)
  146. }
  147. }
  148. aurUp = develUp
  149. }
  150. return aurUp, repoUp, errs.Return()
  151. }
  152. func upDevel(remote []alpm.Package, aurdata map[string]*rpc.Pkg) (toUpgrade upSlice) {
  153. toUpdate := make([]alpm.Package, 0)
  154. toRemove := make([]string, 0)
  155. var mux1 sync.Mutex
  156. var mux2 sync.Mutex
  157. var wg sync.WaitGroup
  158. checkUpdate := func(vcsName string, e shaInfos) {
  159. defer wg.Done()
  160. if e.needsUpdate() {
  161. if _, ok := aurdata[vcsName]; ok {
  162. for _, pkg := range remote {
  163. if pkg.Name() == vcsName {
  164. mux1.Lock()
  165. toUpdate = append(toUpdate, pkg)
  166. mux1.Unlock()
  167. return
  168. }
  169. }
  170. }
  171. mux2.Lock()
  172. toRemove = append(toRemove, vcsName)
  173. mux2.Unlock()
  174. }
  175. }
  176. for vcsName, e := range savedInfo {
  177. wg.Add(1)
  178. go checkUpdate(vcsName, e)
  179. }
  180. wg.Wait()
  181. for _, pkg := range toUpdate {
  182. if pkg.ShouldIgnore() {
  183. printIgnoringPackage(pkg, "latest-commit")
  184. } else {
  185. toUpgrade = append(toUpgrade, upgrade{pkg.Name(), "devel", pkg.Version(), "latest-commit"})
  186. }
  187. }
  188. removeVCSPackage(toRemove)
  189. return
  190. }
  191. // upAUR gathers foreign packages and checks if they have new versions.
  192. // Output: Upgrade type package list.
  193. func upAUR(remote []alpm.Package, aurdata map[string]*rpc.Pkg) (upSlice, error) {
  194. toUpgrade := make(upSlice, 0)
  195. for _, pkg := range remote {
  196. aurPkg, ok := aurdata[pkg.Name()]
  197. if !ok {
  198. continue
  199. }
  200. if (config.TimeUpdate && (int64(aurPkg.LastModified) > pkg.BuildDate().Unix())) ||
  201. (alpm.VerCmp(pkg.Version(), aurPkg.Version) < 0) {
  202. if pkg.ShouldIgnore() {
  203. printIgnoringPackage(pkg, aurPkg.Version)
  204. } else {
  205. toUpgrade = append(toUpgrade, upgrade{aurPkg.Name, "aur", pkg.Version(), aurPkg.Version})
  206. }
  207. }
  208. }
  209. return toUpgrade, nil
  210. }
  211. func printIgnoringPackage(pkg alpm.Package, newPkgVersion string) {
  212. left, right := getVersionDiff(pkg.Version(), newPkgVersion)
  213. fmt.Printf("%s %s: ignoring package upgrade (%s => %s)\n",
  214. yellow(bold(smallArrow)),
  215. cyan(pkg.Name()),
  216. left, right,
  217. )
  218. }
  219. func printLocalNewerThanAUR(
  220. remote []alpm.Package, aurdata map[string]*rpc.Pkg) {
  221. for _, pkg := range remote {
  222. aurPkg, ok := aurdata[pkg.Name()]
  223. if !ok {
  224. continue
  225. }
  226. left, right := getVersionDiff(pkg.Version(), aurPkg.Version)
  227. if !isDevelName(pkg.Name()) && alpm.VerCmp(pkg.Version(), aurPkg.Version) > 0 {
  228. fmt.Printf("%s %s: local (%s) is newer than AUR (%s)\n",
  229. yellow(bold(smallArrow)),
  230. cyan(pkg.Name()),
  231. left, right,
  232. )
  233. }
  234. }
  235. }
  236. // upRepo gathers local packages and checks if they have new versions.
  237. // Output: Upgrade type package list.
  238. func upRepo(local []alpm.Package) (upSlice, error) {
  239. slice := upSlice{}
  240. localDB, err := alpmHandle.LocalDB()
  241. if err != nil {
  242. return slice, err
  243. }
  244. err = alpmHandle.TransInit(alpm.TransFlagNoLock)
  245. if err != nil {
  246. return slice, err
  247. }
  248. defer func() {
  249. err = alpmHandle.TransRelease()
  250. }()
  251. err = alpmHandle.SyncSysupgrade(cmdArgs.existsDouble("u", "sysupgrade"))
  252. if err != nil {
  253. return slice, err
  254. }
  255. _ = alpmHandle.TransGetAdd().ForEach(func(pkg alpm.Package) error {
  256. localVer := "-"
  257. if localPkg := localDB.Pkg(pkg.Name()); localPkg != nil {
  258. localVer = localPkg.Version()
  259. }
  260. slice = append(slice, upgrade{
  261. pkg.Name(),
  262. pkg.DB().Name(),
  263. localVer,
  264. pkg.Version(),
  265. })
  266. return nil
  267. })
  268. return slice, nil
  269. }
  270. // upgradePkgs handles updating the cache and installing updates.
  271. func upgradePkgs(aurUp, repoUp upSlice) (types.StringSet, types.StringSet, error) {
  272. ignore := make(types.StringSet)
  273. aurNames := make(types.StringSet)
  274. allUpLen := len(repoUp) + len(aurUp)
  275. if allUpLen == 0 {
  276. return ignore, aurNames, nil
  277. }
  278. if !config.UpgradeMenu {
  279. for _, pkg := range aurUp {
  280. aurNames.Set(pkg.Name)
  281. }
  282. return ignore, aurNames, nil
  283. }
  284. sort.Sort(repoUp)
  285. sort.Sort(aurUp)
  286. allUp := append(repoUp, aurUp...)
  287. fmt.Printf("%s"+bold(" %d ")+"%s\n", bold(cyan("::")), allUpLen, bold("Packages to upgrade."))
  288. allUp.print()
  289. fmt.Println(bold(green(arrow + " Packages to not upgrade: (eg: 1 2 3, 1-3, ^4 or repo name)")))
  290. fmt.Print(bold(green(arrow + " ")))
  291. numbers, err := getInput(config.AnswerUpgrade)
  292. if err != nil {
  293. return nil, nil, err
  294. }
  295. //upgrade menu asks you which packages to NOT upgrade so in this case
  296. //include and exclude are kind of swapped
  297. //include, exclude, other := parseNumberMenu(string(numberBuf))
  298. include, exclude, otherInclude, otherExclude := types.ParseNumberMenu(numbers)
  299. isInclude := len(exclude) == 0 && len(otherExclude) == 0
  300. for i, pkg := range repoUp {
  301. if isInclude && otherInclude.Get(pkg.Repository) {
  302. ignore.Set(pkg.Name)
  303. }
  304. if isInclude && !include.Get(len(repoUp)-i+len(aurUp)) {
  305. continue
  306. }
  307. if !isInclude && (exclude.Get(len(repoUp)-i+len(aurUp)) || otherExclude.Get(pkg.Repository)) {
  308. continue
  309. }
  310. ignore.Set(pkg.Name)
  311. }
  312. for i, pkg := range aurUp {
  313. if isInclude && otherInclude.Get(pkg.Repository) {
  314. continue
  315. }
  316. if isInclude && !include.Get(len(aurUp)-i) {
  317. aurNames.Set(pkg.Name)
  318. }
  319. if !isInclude && (exclude.Get(len(aurUp)-i) || otherExclude.Get(pkg.Repository)) {
  320. aurNames.Set(pkg.Name)
  321. }
  322. }
  323. return ignore, aurNames, err
  324. }