upgrade.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. "sort"
  7. "strings"
  8. "sync"
  9. alpm "github.com/jguer/go-alpm"
  10. pkgb "github.com/mikkeloscar/gopkgbuild"
  11. )
  12. // upgrade type describes a system upgrade.
  13. type upgrade struct {
  14. Name string
  15. Repository string
  16. LocalVersion string
  17. RemoteVersion string
  18. }
  19. // upSlice is a slice of Upgrades
  20. type upSlice []upgrade
  21. func (u upSlice) Len() int { return len(u) }
  22. func (u upSlice) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
  23. func (u upSlice) Less(i, j int) bool {
  24. if u[i].Repository == u[j].Repository {
  25. iRunes := []rune(u[i].Name)
  26. jRunes := []rune(u[j].Name)
  27. return lessRunes(iRunes, jRunes)
  28. }
  29. syncDb, err := alpmHandle.SyncDbs()
  30. if err != nil {
  31. iRunes := []rune(u[i].Repository)
  32. jRunes := []rune(u[j].Repository)
  33. return lessRunes(iRunes, jRunes)
  34. }
  35. less := false
  36. found := syncDb.ForEach(func(db alpm.Db) error {
  37. if db.Name() == u[i].Repository {
  38. less = true
  39. } else if db.Name() == u[j].Repository {
  40. less = false
  41. } else {
  42. return nil
  43. }
  44. return fmt.Errorf("")
  45. })
  46. if found != nil {
  47. return less
  48. } else {
  49. iRunes := []rune(u[i].Repository)
  50. jRunes := []rune(u[j].Repository)
  51. return lessRunes(iRunes, jRunes)
  52. }
  53. }
  54. func getVersionDiff(oldVersion, newversion string) (left, right string) {
  55. old, errOld := pkgb.NewCompleteVersion(oldVersion)
  56. new, errNew := pkgb.NewCompleteVersion(newversion)
  57. if errOld != nil {
  58. left = red("Invalid Version")
  59. }
  60. if errNew != nil {
  61. right = red("Invalid Version")
  62. }
  63. if errOld == nil && errNew == nil {
  64. if old.Version == new.Version {
  65. left = string(old.Version) + "-" + red(string(old.Pkgrel))
  66. right = string(new.Version) + "-" + green(string(new.Pkgrel))
  67. } else {
  68. left = red(string(old.Version)) + "-" + string(old.Pkgrel)
  69. right = bold(green(string(new.Version))) + "-" + string(new.Pkgrel)
  70. }
  71. }
  72. return
  73. }
  74. // upList returns lists of packages to upgrade from each source.
  75. func upList(dt *depTree) (aurUp upSlice, repoUp upSlice, err error) {
  76. local, remote, _, remoteNames, err := filterPackages()
  77. if err != nil {
  78. return nil, nil, err
  79. }
  80. var wg sync.WaitGroup
  81. var develUp upSlice
  82. var repoErr error
  83. var aurErr error
  84. var develErr error
  85. fmt.Println(bold(cyan("::") + " Searching databases for updates..."))
  86. wg.Add(1)
  87. go func() {
  88. repoUp, repoErr = upRepo(local)
  89. wg.Done()
  90. }()
  91. fmt.Println(bold(cyan("::") + " Searching AUR for updates..."))
  92. wg.Add(1)
  93. go func() {
  94. aurUp, aurErr = upAUR(remote, remoteNames, dt)
  95. wg.Done()
  96. }()
  97. if config.Devel {
  98. fmt.Println(bold(cyan("::") + " Checking development packages..."))
  99. wg.Add(1)
  100. go func() {
  101. develUp, develErr = upDevel(remote)
  102. wg.Done()
  103. }()
  104. }
  105. wg.Wait()
  106. errs := make([]string, 0)
  107. for _, e := range []error{repoErr, aurErr, develErr} {
  108. if e != nil {
  109. errs = append(errs, e.Error())
  110. }
  111. }
  112. if len(errs) > 0 {
  113. err = fmt.Errorf("%s", strings.Join(errs, "\n"))
  114. return nil, nil, err
  115. }
  116. if develUp != nil {
  117. names := make(stringSet)
  118. for _, up := range develUp {
  119. names.set(up.Name)
  120. }
  121. for _, up := range aurUp {
  122. if !names.get(up.Name) {
  123. develUp = append(develUp, up)
  124. }
  125. }
  126. aurUp = develUp
  127. }
  128. return aurUp, repoUp, err
  129. }
  130. func upDevel(remote []alpm.Package) (toUpgrade upSlice, err error) {
  131. toUpdate := make([]alpm.Package, 0, 0)
  132. toRemove := make([]string, 0, 0)
  133. var mux1 sync.Mutex
  134. var mux2 sync.Mutex
  135. var wg sync.WaitGroup
  136. checkUpdate := func(vcsName string, e shaInfos) {
  137. defer wg.Done()
  138. if e.needsUpdate() {
  139. for _, pkg := range remote {
  140. if pkg.Name() == vcsName {
  141. mux1.Lock()
  142. toUpdate = append(toUpdate, pkg)
  143. mux1.Unlock()
  144. return
  145. }
  146. }
  147. mux2.Lock()
  148. toRemove = append(toRemove, vcsName)
  149. mux2.Unlock()
  150. }
  151. }
  152. for vcsName, e := range savedInfo {
  153. wg.Add(1)
  154. go checkUpdate(vcsName, e)
  155. }
  156. wg.Wait()
  157. for _, pkg := range toUpdate {
  158. if pkg.ShouldIgnore() {
  159. left, right := getVersionDiff(pkg.Version(), "latest-commit")
  160. fmt.Print(magenta("Warning: "))
  161. fmt.Printf("%s ignoring package upgrade (%s => %s)\n", cyan(pkg.Name()), left, right)
  162. } else {
  163. toUpgrade = append(toUpgrade, upgrade{pkg.Name(), "devel", pkg.Version(), "latest-commit"})
  164. }
  165. }
  166. removeVCSPackage(toRemove)
  167. return
  168. }
  169. // upAUR gathers foreign packages and checks if they have new versions.
  170. // Output: Upgrade type package list.
  171. func upAUR(remote []alpm.Package, remoteNames []string, dt *depTree) (toUpgrade upSlice, err error) {
  172. for _, pkg := range remote {
  173. aurPkg, ok := dt.Aur[pkg.Name()]
  174. if !ok {
  175. continue
  176. }
  177. if (config.TimeUpdate && (int64(aurPkg.LastModified) > pkg.BuildDate().Unix())) ||
  178. (alpm.VerCmp(pkg.Version(), aurPkg.Version) < 0) {
  179. if pkg.ShouldIgnore() {
  180. left, right := getVersionDiff(pkg.Version(), aurPkg.Version)
  181. fmt.Print(magenta("Warning: "))
  182. fmt.Printf("%s ignoring package upgrade (%s => %s)\n", cyan(pkg.Name()), left, right)
  183. } else {
  184. toUpgrade = append(toUpgrade, upgrade{aurPkg.Name, "aur", pkg.Version(), aurPkg.Version})
  185. }
  186. }
  187. }
  188. return
  189. }
  190. // upRepo gathers local packages and checks if they have new versions.
  191. // Output: Upgrade type package list.
  192. func upRepo(local []alpm.Package) (upSlice, error) {
  193. dbList, err := alpmHandle.SyncDbs()
  194. if err != nil {
  195. return nil, err
  196. }
  197. slice := upSlice{}
  198. for _, pkg := range local {
  199. newPkg := pkg.NewVersion(dbList)
  200. if newPkg != nil {
  201. if pkg.ShouldIgnore() {
  202. left, right := getVersionDiff(pkg.Version(), newPkg.Version())
  203. fmt.Print(magenta("Warning: "))
  204. fmt.Printf("%s ignoring package upgrade (%s => %s)\n", cyan(pkg.Name()), left, right)
  205. } else {
  206. slice = append(slice, upgrade{pkg.Name(), newPkg.DB().Name(), pkg.Version(), newPkg.Version()})
  207. }
  208. }
  209. }
  210. return slice, nil
  211. }
  212. // upgradePkgs handles updating the cache and installing updates.
  213. func upgradePkgs(dt *depTree) (stringSet, stringSet, error) {
  214. ignore := make(stringSet)
  215. aurNames := make(stringSet)
  216. aurUp, repoUp, err := upList(dt)
  217. if err != nil {
  218. return ignore, aurNames, err
  219. } else if len(aurUp)+len(repoUp) == 0 {
  220. return ignore, aurNames, err
  221. }
  222. sort.Sort(repoUp)
  223. sort.Sort(aurUp)
  224. fmt.Println(bold(blue("::")), len(aurUp)+len(repoUp), bold("Packages to upgrade."))
  225. repoUp.Print(len(aurUp) + 1)
  226. aurUp.Print(1)
  227. if config.NoConfirm {
  228. for _, up := range aurUp {
  229. aurNames.set(up.Name)
  230. }
  231. return ignore, aurNames, nil
  232. }
  233. fmt.Println(bold(green(arrow + " Packages to not upgrade (eg: 1 2 3, 1-3, ^4 or repo name)")))
  234. fmt.Print(bold(green(arrow + " ")))
  235. reader := bufio.NewReader(os.Stdin)
  236. numberBuf, overflow, err := reader.ReadLine()
  237. if err != nil {
  238. return nil, nil, err
  239. }
  240. if overflow {
  241. return nil, nil, fmt.Errorf("Input too long")
  242. }
  243. //upgrade menu asks you which packages to NOT upgrade so in this case
  244. //include and exclude are kind of swaped
  245. //include, exclude, other := parseNumberMenu(string(numberBuf))
  246. include, exclude, otherInclude, otherExclude := parseNumberMenu(string(numberBuf))
  247. isInclude := len(exclude) == 0 && len(otherExclude) == 0
  248. for i, pkg := range repoUp {
  249. if isInclude && otherInclude.get(pkg.Repository) {
  250. ignore.set(pkg.Name)
  251. }
  252. if isInclude && !include.get(len(repoUp)-i+len(aurUp)) {
  253. continue
  254. }
  255. if !isInclude && (exclude.get(len(repoUp)-i+len(aurUp)) || otherExclude.get(pkg.Repository)) {
  256. continue
  257. }
  258. ignore.set(pkg.Name)
  259. }
  260. for i, pkg := range aurUp {
  261. if isInclude && otherInclude.get(pkg.Repository) {
  262. continue
  263. }
  264. if isInclude && !include.get(len(aurUp)-i) {
  265. aurNames.set(pkg.Name)
  266. }
  267. if !isInclude && (exclude.get(len(aurUp)-i) || otherExclude.get(pkg.Repository)) {
  268. aurNames.set(pkg.Name)
  269. }
  270. }
  271. return ignore, aurNames, err
  272. }