yay/aur/aur.go

395 lines
9.3 KiB
Go
Raw Normal View History

2016-09-13 03:06:24 +02:00
package aur
import (
2016-10-02 22:50:23 +02:00
"bytes"
2016-09-13 03:06:24 +02:00
"fmt"
"os"
"os/exec"
"sort"
"strings"
2016-10-17 00:02:48 +02:00
2016-11-30 18:55:56 +01:00
"github.com/jguer/yay/pacman"
2016-09-13 03:06:24 +02:00
)
2016-11-30 18:55:56 +01:00
// TarBin describes the default installation point of tar command.
2016-09-13 03:06:24 +02:00
const TarBin string = "/usr/bin/tar"
// BaseURL givers the AUR default address.
2016-09-13 03:06:24 +02:00
const BaseURL string = "https://aur.archlinux.org"
// MakepkgBin describes the default installation point of makepkg command.
2016-09-13 03:06:24 +02:00
const MakepkgBin string = "/usr/bin/makepkg"
// SearchMode is search without numbers.
const SearchMode int = -1
// NoConfirm ignores prompts.
var NoConfirm = false
2016-12-02 13:19:03 +01:00
// SortMode determines top down package or down top package display
var SortMode = DownTop
// Describes Sorting method for numberdisplay
const (
DownTop = iota
TopDown
)
// Result describes an AUR package.
2016-09-13 03:06:24 +02:00
type Result struct {
2016-12-01 00:08:28 +01:00
ID int `json:"ID"`
Name string `json:"Name"`
PackageBaseID int `json:"PackageBaseID"`
PackageBase string `json:"PackageBase"`
Version string `json:"Version"`
Description string `json:"Description"`
URL string `json:"URL"`
NumVotes int `json:"NumVotes"`
Popularity float32 `json:"Popularity"`
OutOfDate int `json:"OutOfDate"`
Maintainer string `json:"Maintainer"`
FirstSubmitted int `json:"FirstSubmitted"`
LastModified int64 `json:"LastModified"`
URLPath string `json:"URLPath"`
Installed bool
2016-11-30 18:55:56 +01:00
Depends []string `json:"Depends"`
MakeDepends []string `json:"MakeDepends"`
OptDepends []string `json:"OptDepends"`
Conflicts []string `json:"Conflicts"`
2016-12-01 00:08:28 +01:00
Provides []string `json:"Provides"`
2016-11-30 18:55:56 +01:00
License []string `json:"License"`
Keywords []string `json:"Keywords"`
2016-09-13 03:06:24 +02:00
}
2016-11-30 18:55:56 +01:00
// Query is a collection of Results
type Query []Result
2016-09-13 03:06:24 +02:00
2016-11-30 18:55:56 +01:00
func (q Query) Len() int {
return len(q)
2016-09-13 03:06:24 +02:00
}
2016-11-30 18:55:56 +01:00
func (q Query) Less(i, j int) bool {
2016-12-02 13:19:03 +01:00
if SortMode == DownTop {
return q[i].NumVotes < q[j].NumVotes
}
return q[i].NumVotes > q[j].NumVotes
2016-09-13 03:06:24 +02:00
}
2016-11-30 18:55:56 +01:00
func (q Query) Swap(i, j int) {
q[i], q[j] = q[j], q[i]
2016-09-13 03:06:24 +02:00
}
2016-09-13 18:43:56 +02:00
// PrintSearch handles printing search results in a given format
2016-11-30 18:55:56 +01:00
func (q Query) PrintSearch(start int) {
for i, res := range q {
2016-12-02 13:19:03 +01:00
var toprint string
if start != SearchMode {
if SortMode == DownTop {
toprint += fmt.Sprintf("%d ", len(q)+start-i-1)
} else {
toprint += fmt.Sprintf("%d ", start+i)
}
}
toprint += fmt.Sprintf("\x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[0m(%d) ", "aur", res.Name, res.Version, res.NumVotes)
if res.Installed == true {
toprint += fmt.Sprintf("\x1b[32;40mInstalled\x1b[0m")
2016-09-13 18:43:56 +02:00
}
2016-12-02 13:19:03 +01:00
toprint += "\n" + res.Description
fmt.Println(toprint)
2016-09-13 18:43:56 +02:00
}
}
2016-11-30 18:55:56 +01:00
// Search returns an AUR search
func Search(pkg string, sortS bool) (Query, int, error) {
type returned struct {
Results Query `json:"results"`
ResultCount int `json:"resultcount"`
2016-09-13 03:06:24 +02:00
}
2016-11-30 18:55:56 +01:00
r := returned{}
err := getJSON("https://aur.archlinux.org/rpc/?v=5&type=search&arg="+pkg, &r)
2016-09-13 03:06:24 +02:00
2016-11-30 18:55:56 +01:00
if sortS {
sort.Sort(r.Results)
2016-09-13 03:06:24 +02:00
}
2016-12-02 13:19:03 +01:00
setter := pacman.PFactory(pFSetTrue)
2016-09-13 03:06:24 +02:00
2016-12-02 13:19:03 +01:00
for i, res := range r.Results {
if i == len(r.Results)-1 {
setter(res.Name, &r.Results[i], true)
continue
}
setter(res.Name, &r.Results[i], false)
}
2016-11-30 18:55:56 +01:00
return r.Results, r.ResultCount, err
}
2016-12-02 13:19:03 +01:00
// This is very dirty but it works so good.
func pFSetTrue(res interface{}) {
f, ok := res.(*Result)
if !ok {
fmt.Println("Unable to convert back to Result")
return
}
f.Installed = true
return
}
2016-11-30 18:55:56 +01:00
// Info returns an AUR search with package details
func Info(pkg string) (Query, int, error) {
type returned struct {
Results Query `json:"results"`
ResultCount int `json:"resultcount"`
}
r := returned{}
err := getJSON("https://aur.archlinux.org/rpc/?v=5&type=info&arg[]="+pkg, &r)
return r.Results, r.ResultCount, err
}
// MultiInfo takes a slice of strings and returns a slice with the info of each package
func MultiInfo(pkgS []string) (Query, int, error) {
type returned struct {
Results Query `json:"results"`
ResultCount int `json:"resultcount"`
}
r := returned{}
var pkg string
for _, pkgn := range pkgS {
pkg += "&arg[]=" + pkgn
}
err := getJSON("https://aur.archlinux.org/rpc/?v=5&type=info"+pkg, &r)
return r.Results, r.ResultCount, err
}
2016-11-30 18:55:56 +01:00
// Install sends system commands to make and install a package from pkgName
2016-12-01 00:21:56 +01:00
func Install(pkg string, baseDir string, flags []string) (err error) {
2016-11-30 18:55:56 +01:00
q, n, err := Info(pkg)
2016-09-13 03:06:24 +02:00
if err != nil {
2016-11-30 18:55:56 +01:00
return
2016-09-13 03:06:24 +02:00
}
2016-11-30 18:55:56 +01:00
if n == 0 {
return fmt.Errorf("Package %s does not exist", pkg)
}
2016-12-01 00:08:28 +01:00
q[0].Install(baseDir, flags)
2016-11-30 18:55:56 +01:00
return err
2016-09-13 03:06:24 +02:00
}
2016-11-30 18:55:56 +01:00
// Upgrade tries to update every foreign package installed in the system
2016-12-01 00:21:56 +01:00
func Upgrade(baseDir string, flags []string) error {
2016-11-30 18:55:56 +01:00
fmt.Println("\x1b[1;36;1m::\x1b[0m\x1b[1m Starting AUR upgrade...\x1b[0m")
foreign, n, err := pacman.ForeignPackages()
if err != nil || n == 0 {
2016-09-13 03:06:24 +02:00
return err
}
keys := make([]string, len(foreign))
i := 0
for k := range foreign {
keys[i] = k
i++
}
2016-11-30 18:55:56 +01:00
q, _, err := MultiInfo(keys)
if err != nil {
return err
}
2016-11-30 18:55:56 +01:00
outdated := q[:0]
for _, res := range q {
if _, ok := foreign[res.Name]; ok {
2016-11-30 18:55:56 +01:00
// Leaving this here for now, warn about downgrades later
if res.LastModified > foreign[res.Name].Date {
fmt.Printf("\x1b[1m\x1b[32m==>\x1b[33;1m %s: \x1b[0m%s \x1b[33;1m-> \x1b[0m%s\n",
res.Name, foreign[res.Name].Version, res.Version)
outdated = append(outdated, res)
2016-11-30 18:55:56 +01:00
}
}
}
2016-09-13 03:06:24 +02:00
2016-11-30 18:55:56 +01:00
//If there are no outdated packages, don't prompt
if len(outdated) == 0 {
fmt.Println(" there is nothing to do")
return nil
}
2016-09-13 03:06:24 +02:00
2016-11-30 18:55:56 +01:00
// Install updated packages
if !NoConfirm {
2016-11-30 18:55:56 +01:00
fmt.Println("\x1b[1m\x1b[32m==> Proceed with upgrade\x1b[0m\x1b[1m (Y/n)\x1b[0m")
var response string
fmt.Scanln(&response)
if strings.ContainsAny(response, "n & N") {
return nil
}
2016-09-13 03:06:24 +02:00
}
2016-11-30 18:55:56 +01:00
for _, pkg := range outdated {
2016-12-01 00:08:28 +01:00
pkg.Install(baseDir, flags)
2016-09-13 03:06:24 +02:00
}
2016-11-30 18:55:56 +01:00
return nil
2016-09-13 03:06:24 +02:00
}
// Install handles install from Result
2016-12-01 00:08:28 +01:00
func (a *Result) Install(baseDir string, flags []string) (err error) {
2016-11-04 11:52:11 +01:00
fmt.Printf("\x1b[1m\x1b[32m==> Installing\x1b[33m %s\x1b[0m\n", a.Name)
2016-09-13 03:06:24 +02:00
// No need to use filepath.separators because it won't run on inferior platforms
err = os.MkdirAll(baseDir+"builds", 0755)
if err != nil {
fmt.Println(err)
return
}
tarLocation := baseDir + a.Name + ".tar.gz"
defer os.Remove(baseDir + a.Name + ".tar.gz")
err = downloadFile(tarLocation, BaseURL+a.URLPath)
if err != nil {
return
}
err = exec.Command(TarBin, "-xf", tarLocation, "-C", baseDir).Run()
if err != nil {
return
}
defer os.RemoveAll(baseDir + a.Name)
var response string
2016-10-02 22:50:23 +02:00
var dir bytes.Buffer
dir.WriteString(baseDir)
dir.WriteString(a.Name)
dir.WriteString("/")
if !NoConfirm {
2016-10-17 00:02:48 +02:00
fmt.Println("\x1b[1m\x1b[32m==> Edit PKGBUILD?\x1b[0m\x1b[1m (y/N)\x1b[0m")
2016-10-05 02:19:13 +02:00
fmt.Scanln(&response)
if strings.ContainsAny(response, "y & Y") {
editcmd := exec.Command(Editor, dir.String()+"PKGBUILD")
editcmd.Stdout = os.Stdout
editcmd.Stderr = os.Stderr
editcmd.Stdin = os.Stdin
2016-12-01 00:08:28 +01:00
editcmd.Run()
2016-10-05 02:19:13 +02:00
}
}
2016-12-01 00:08:28 +01:00
aurDeps, repoDeps, err := a.Dependencies()
2016-11-04 11:52:11 +01:00
if err != nil {
return
}
2016-10-02 22:50:23 +02:00
printDependencies(aurDeps, repoDeps)
if !NoConfirm && (len(aurDeps) != 0 || len(repoDeps) != 0) {
fmt.Println("\x1b[1m\x1b[32m==> Continue?\x1b[0m\x1b[1m (Y/n)\x1b[0m")
fmt.Scanln(&response)
if strings.ContainsAny(response, "n & N") {
return fmt.Errorf("user did not like the dependencies")
}
}
2016-12-01 00:08:28 +01:00
aurQ, n, err := MultiInfo(aurDeps)
if n != len(aurDeps) {
missingDeps(aurDeps, aurQ)
if !NoConfirm {
fmt.Println("\x1b[1m\x1b[32m==> Continue?\x1b[0m\x1b[1m (Y/n)\x1b[0m")
fmt.Scanln(&response)
if strings.ContainsAny(response, "n & N") {
return fmt.Errorf("unable to install dependencies")
}
}
2016-12-01 00:08:28 +01:00
}
2016-11-15 02:46:11 +01:00
2016-12-01 00:08:28 +01:00
// Handle AUR dependencies first
for _, dep := range aurQ {
errA := dep.Install(baseDir, []string{"--asdeps", "--noconfirm"})
if errA != nil {
return errA
}
2016-11-15 02:46:11 +01:00
}
2016-12-01 00:08:28 +01:00
// Repo dependencies
2016-12-01 20:57:53 +01:00
if len(repoDeps) != 0 {
errR := pacman.Install(repoDeps, []string{"--asdeps", "--noconfirm"})
if errR != nil {
pacman.CleanRemove(aurDeps)
return errR
}
2016-12-01 20:57:53 +01:00
}
2016-12-01 00:08:28 +01:00
2016-10-02 22:50:23 +02:00
err = os.Chdir(dir.String())
2016-09-13 03:06:24 +02:00
if err != nil {
return
}
var makepkgcmd *exec.Cmd
var args []string
args = append(args, "-sri")
args = append(args, flags...)
makepkgcmd = exec.Command(MakepkgBin, args...)
2016-09-13 03:06:24 +02:00
makepkgcmd.Stdout = os.Stdout
makepkgcmd.Stderr = os.Stderr
makepkgcmd.Stdin = os.Stdin
err = makepkgcmd.Run()
return
}
func printDependencies(aurDeps []string, repoDeps []string) {
if len(repoDeps) != 0 {
fmt.Print("\x1b[1m\x1b[32m==> Repository dependencies: \x1b[0m")
for _, repoD := range repoDeps {
fmt.Print("\x1b[33m", repoD, " \x1b[0m")
}
fmt.Print("\n")
}
if len(repoDeps) != 0 {
fmt.Print("\x1b[1m\x1b[32m==> AUR dependencies: \x1b[0m")
for _, aurD := range aurDeps {
fmt.Print("\x1b[33m", aurD, " \x1b[0m")
}
fmt.Print("\n")
}
}
func missingDeps(aurDeps []string, aurQ Query) {
for _, depName := range aurDeps {
found := false
for _, dep := range aurQ {
if dep.Name == depName {
found = true
break
}
}
if !found {
fmt.Println("\x1b[31mUnable to find", depName, "in AUR\x1b[0m")
}
}
return
}
2016-12-01 00:08:28 +01:00
// Dependencies returns package dependencies not installed belonging to AUR
func (a *Result) Dependencies() (aur []string, repo []string, err error) {
var q Query
if len(a.Depends) == 0 && len(a.MakeDepends) == 0 {
var n int
q, n, err = Info(a.Name)
if n == 0 || err != nil {
err = fmt.Errorf("Unable to search dependencies, %s", err)
return
2016-09-13 03:06:24 +02:00
}
2016-12-01 00:08:28 +01:00
} else {
q = append(q, *a)
2016-09-13 03:06:24 +02:00
}
2016-12-01 00:08:28 +01:00
aur, repo, err = pacman.OutofRepo(append(q[0].MakeDepends, q[0].Depends...))
2016-09-13 03:06:24 +02:00
return
}