yay/upgrade.go
morganamilo a04dd94282
Only pass needed upgrades to deptree
To know what AUR packages need updating a rpc request is needed for all
packages. The dep tree is designed to cache everything to minimize the
amount of rpc requests. The downside of this is the dep tree ends up
with all sorts of packages in cache that it doesn't need. Then the
deptree tries to resolve deps for all of thoes packages.

By spliting the sysupgrade from the dep tree this stops this from
happening, it uses one more rpc request but also may lower the amount of
total rpc requests needed lated on.

This fixes a couple of tiny bugs such as triggering providers prompts
and printing AUR out of date messages for packages that are not going
to be installed.

This also fixes another display bug where repo packages from -Su would
not apear when printing the packages to be installed under [Repo].
2018-03-29 19:05:38 +01:00

341 lines
7.6 KiB
Go

package main
import (
"bufio"
"fmt"
"os"
"sort"
"strings"
"sync"
alpm "github.com/jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
pkgb "github.com/mikkeloscar/gopkgbuild"
)
// upgrade type describes a system upgrade.
type upgrade struct {
Name string
Repository string
LocalVersion string
RemoteVersion string
}
// upSlice is a slice of Upgrades
type upSlice []upgrade
func (u upSlice) Len() int { return len(u) }
func (u upSlice) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
func (u upSlice) Less(i, j int) bool {
if u[i].Repository == u[j].Repository {
iRunes := []rune(u[i].Name)
jRunes := []rune(u[j].Name)
return lessRunes(iRunes, jRunes)
}
syncDb, err := alpmHandle.SyncDbs()
if err != nil {
iRunes := []rune(u[i].Repository)
jRunes := []rune(u[j].Repository)
return lessRunes(iRunes, jRunes)
}
less := false
found := syncDb.ForEach(func(db alpm.Db) error {
if db.Name() == u[i].Repository {
less = true
} else if db.Name() == u[j].Repository {
less = false
} else {
return nil
}
return fmt.Errorf("")
})
if found != nil {
return less
} else {
iRunes := []rune(u[i].Repository)
jRunes := []rune(u[j].Repository)
return lessRunes(iRunes, jRunes)
}
}
func getVersionDiff(oldVersion, newversion string) (left, right string) {
old, errOld := pkgb.NewCompleteVersion(oldVersion)
new, errNew := pkgb.NewCompleteVersion(newversion)
if errOld != nil {
left = red("Invalid Version")
}
if errNew != nil {
right = red("Invalid Version")
}
if errOld == nil && errNew == nil {
if old.Version == new.Version {
left = string(old.Version) + "-" + red(string(old.Pkgrel))
right = string(new.Version) + "-" + green(string(new.Pkgrel))
} else {
left = red(string(old.Version)) + "-" + string(old.Pkgrel)
right = bold(green(string(new.Version))) + "-" + string(new.Pkgrel)
}
}
return
}
// upList returns lists of packages to upgrade from each source.
func upList() (aurUp upSlice, repoUp upSlice, err error) {
local, remote, _, remoteNames, err := filterPackages()
if err != nil {
return nil, nil, err
}
var wg sync.WaitGroup
var develUp upSlice
var repoErr error
var aurErr error
var develErr error
fmt.Println(bold(cyan("::") + " Searching databases for updates..."))
wg.Add(1)
go func() {
repoUp, repoErr = upRepo(local)
wg.Done()
}()
fmt.Println(bold(cyan("::") + " Searching AUR for updates..."))
wg.Add(1)
go func() {
aurUp, aurErr = upAUR(remote, remoteNames)
wg.Done()
}()
if config.Devel {
fmt.Println(bold(cyan("::") + " Checking development packages..."))
wg.Add(1)
go func() {
develUp, develErr = upDevel(remote)
wg.Done()
}()
}
wg.Wait()
errs := make([]string, 0)
for _, e := range []error{repoErr, aurErr, develErr} {
if e != nil {
errs = append(errs, e.Error())
}
}
if len(errs) > 0 {
err = fmt.Errorf("%s", strings.Join(errs, "\n"))
return nil, nil, err
}
if develUp != nil {
names := make(stringSet)
for _, up := range develUp {
names.set(up.Name)
}
for _, up := range aurUp {
if !names.get(up.Name) {
develUp = append(develUp, up)
}
}
aurUp = develUp
}
return aurUp, repoUp, err
}
func upDevel(remote []alpm.Package) (toUpgrade upSlice, err error) {
toUpdate := make([]alpm.Package, 0, 0)
toRemove := make([]string, 0, 0)
var mux1 sync.Mutex
var mux2 sync.Mutex
var wg sync.WaitGroup
checkUpdate := func(vcsName string, e shaInfos) {
defer wg.Done()
if e.needsUpdate() {
for _, pkg := range remote {
if pkg.Name() == vcsName {
mux1.Lock()
toUpdate = append(toUpdate, pkg)
mux1.Unlock()
return
}
}
mux2.Lock()
toRemove = append(toRemove, vcsName)
mux2.Unlock()
}
}
for vcsName, e := range savedInfo {
wg.Add(1)
go checkUpdate(vcsName, e)
}
wg.Wait()
for _, pkg := range toUpdate {
if pkg.ShouldIgnore() {
left, right := getVersionDiff(pkg.Version(), "latest-commit")
fmt.Print(magenta("Warning: "))
fmt.Printf("%s ignoring package upgrade (%s => %s)\n", cyan(pkg.Name()), left, right)
} else {
toUpgrade = append(toUpgrade, upgrade{pkg.Name(), "devel", pkg.Version(), "latest-commit"})
}
}
removeVCSPackage(toRemove)
return
}
// upAUR gathers foreign packages and checks if they have new versions.
// Output: Upgrade type package list.
func upAUR(remote []alpm.Package, remoteNames []string) (upSlice, error) {
toUpgrade := make(upSlice, 0)
_pkgdata, err := aurInfo(remoteNames)
if err != nil {
return nil, err
}
pkgdata := make(map[string]*rpc.Pkg)
for _, pkg := range _pkgdata {
pkgdata[pkg.Name] = pkg
}
for _, pkg := range remote {
aurPkg, ok := pkgdata[pkg.Name()]
if !ok {
continue
}
if (config.TimeUpdate && (int64(aurPkg.LastModified) > pkg.BuildDate().Unix())) ||
(alpm.VerCmp(pkg.Version(), aurPkg.Version) < 0) {
if pkg.ShouldIgnore() {
left, right := getVersionDiff(pkg.Version(), aurPkg.Version)
fmt.Print(magenta("Warning: "))
fmt.Printf("%s ignoring package upgrade (%s => %s)\n", cyan(pkg.Name()), left, right)
} else {
toUpgrade = append(toUpgrade, upgrade{aurPkg.Name, "aur", pkg.Version(), aurPkg.Version})
}
}
}
return toUpgrade, nil
}
// upRepo gathers local packages and checks if they have new versions.
// Output: Upgrade type package list.
func upRepo(local []alpm.Package) (upSlice, error) {
dbList, err := alpmHandle.SyncDbs()
if err != nil {
return nil, err
}
slice := upSlice{}
for _, pkg := range local {
newPkg := pkg.NewVersion(dbList)
if newPkg != nil {
if pkg.ShouldIgnore() {
left, right := getVersionDiff(pkg.Version(), newPkg.Version())
fmt.Print(magenta("Warning: "))
fmt.Printf("%s ignoring package upgrade (%s => %s)\n", cyan(pkg.Name()), left, right)
} else {
slice = append(slice, upgrade{pkg.Name(), newPkg.DB().Name(), pkg.Version(), newPkg.Version()})
}
}
}
return slice, nil
}
// upgradePkgs handles updating the cache and installing updates.
func upgradePkgs(aurUp, repoUp upSlice) (stringSet, stringSet, error) {
ignore := make(stringSet)
aurNames := make(stringSet)
if len(aurUp)+len(repoUp) == 0 {
return ignore, aurNames, nil
}
sort.Sort(repoUp)
sort.Sort(aurUp)
fmt.Println(bold(blue("::")), len(aurUp)+len(repoUp), bold("Packages to upgrade."))
repoUp.Print(len(aurUp) + 1)
aurUp.Print(1)
if config.NoConfirm {
for _, up := range aurUp {
aurNames.set(up.Name)
}
return ignore, aurNames, nil
}
fmt.Println(bold(green(arrow + " Packages to not upgrade (eg: 1 2 3, 1-3, ^4 or repo name)")))
fmt.Print(bold(green(arrow + " ")))
reader := bufio.NewReader(os.Stdin)
numberBuf, overflow, err := reader.ReadLine()
if err != nil {
return nil, nil, err
}
if overflow {
return nil, nil, fmt.Errorf("Input too long")
}
//upgrade menu asks you which packages to NOT upgrade so in this case
//include and exclude are kind of swaped
//include, exclude, other := parseNumberMenu(string(numberBuf))
include, exclude, otherInclude, otherExclude := parseNumberMenu(string(numberBuf))
isInclude := len(exclude) == 0 && len(otherExclude) == 0
for i, pkg := range repoUp {
if isInclude && otherInclude.get(pkg.Repository) {
ignore.set(pkg.Name)
}
if isInclude && !include.get(len(repoUp)-i+len(aurUp)) {
continue
}
if !isInclude && (exclude.get(len(repoUp)-i+len(aurUp)) || otherExclude.get(pkg.Repository)) {
continue
}
ignore.set(pkg.Name)
}
for i, pkg := range aurUp {
if isInclude && otherInclude.get(pkg.Repository) {
continue
}
if isInclude && !include.get(len(aurUp)-i) {
aurNames.set(pkg.Name)
}
if !isInclude && (exclude.get(len(aurUp)-i) || otherExclude.get(pkg.Repository)) {
aurNames.set(pkg.Name)
}
}
return ignore, aurNames, err
}