123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- package aur
- import (
- "bytes"
- "fmt"
- "os"
- "os/exec"
- "sort"
- "strings"
- "github.com/jguer/yay/pacman"
- )
- // TarBin describes the default installation point of tar command.
- const TarBin string = "/usr/bin/tar"
- // BaseURL givers the AUR default address.
- const BaseURL string = "https://aur.archlinux.org"
- // MakepkgBin describes the default installation point of makepkg command.
- const MakepkgBin string = "/usr/bin/makepkg"
- // SearchMode is search without numbers.
- const SearchMode int = -1
- // NoConfirm ignores prompts.
- var NoConfirm = false
- // 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.
- type Result struct {
- 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
- Depends []string `json:"Depends"`
- MakeDepends []string `json:"MakeDepends"`
- OptDepends []string `json:"OptDepends"`
- Conflicts []string `json:"Conflicts"`
- Provides []string `json:"Provides"`
- License []string `json:"License"`
- Keywords []string `json:"Keywords"`
- }
- // Query is a collection of Results
- type Query []Result
- func (q Query) Len() int {
- return len(q)
- }
- func (q Query) Less(i, j int) bool {
- if SortMode == DownTop {
- return q[i].NumVotes < q[j].NumVotes
- }
- return q[i].NumVotes > q[j].NumVotes
- }
- func (q Query) Swap(i, j int) {
- q[i], q[j] = q[j], q[i]
- }
- // PrintSearch handles printing search results in a given format
- func (q Query) PrintSearch(start int) {
- for i, res := range q {
- 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")
- }
- toprint += "\n" + res.Description
- fmt.Println(toprint)
- }
- }
- // 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"`
- }
- r := returned{}
- err := getJSON("https://aur.archlinux.org/rpc/?v=5&type=search&arg="+pkg, &r)
- if sortS {
- sort.Sort(r.Results)
- }
- setter := pacman.PFactory(pFSetTrue)
- 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)
- }
- return r.Results, r.ResultCount, err
- }
- // 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
- }
- // 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
- }
- // Install sends system commands to make and install a package from pkgName
- func Install(pkg string, baseDir string, flags []string) (err error) {
- q, n, err := Info(pkg)
- if err != nil {
- return
- }
- if n == 0 {
- return fmt.Errorf("Package %s does not exist", pkg)
- }
- q[0].Install(baseDir, flags)
- return err
- }
- // Upgrade tries to update every foreign package installed in the system
- func Upgrade(baseDir string, flags []string) error {
- 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 {
- return err
- }
- keys := make([]string, len(foreign))
- i := 0
- for k := range foreign {
- keys[i] = k
- i++
- }
- q, _, err := MultiInfo(keys)
- if err != nil {
- return err
- }
- outdated := q[:0]
- for _, res := range q {
- if _, ok := foreign[res.Name]; ok {
- // 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)
- }
- }
- }
- //If there are no outdated packages, don't prompt
- if len(outdated) == 0 {
- fmt.Println(" there is nothing to do")
- return nil
- }
- // Install updated packages
- if !NoConfirm {
- 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
- }
- }
- for _, pkg := range outdated {
- pkg.Install(baseDir, flags)
- }
- return nil
- }
- // Install handles install from Result
- func (a *Result) Install(baseDir string, flags []string) (err error) {
- fmt.Printf("\x1b[1m\x1b[32m==> Installing\x1b[33m %s\x1b[0m\n", a.Name)
- // 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
- var dir bytes.Buffer
- dir.WriteString(baseDir)
- dir.WriteString(a.Name)
- dir.WriteString("/")
- if !NoConfirm {
- fmt.Println("\x1b[1m\x1b[32m==> Edit PKGBUILD?\x1b[0m\x1b[1m (y/N)\x1b[0m")
- 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
- editcmd.Run()
- }
- }
- aurDeps, repoDeps, err := a.Dependencies()
- if err != nil {
- return
- }
- 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")
- }
- }
- 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")
- }
- }
- }
- // Handle AUR dependencies first
- for _, dep := range aurQ {
- errA := dep.Install(baseDir, []string{"--asdeps", "--noconfirm"})
- if errA != nil {
- return errA
- }
- }
- // Repo dependencies
- if len(repoDeps) != 0 {
- errR := pacman.Install(repoDeps, []string{"--asdeps", "--noconfirm"})
- if errR != nil {
- pacman.CleanRemove(aurDeps)
- return errR
- }
- }
- err = os.Chdir(dir.String())
- if err != nil {
- return
- }
- var makepkgcmd *exec.Cmd
- var args []string
- args = append(args, "-sri")
- args = append(args, flags...)
- makepkgcmd = exec.Command(MakepkgBin, args...)
- 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
- }
- // 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
- }
- } else {
- q = append(q, *a)
- }
- aur, repo, err = pacman.OutofRepo(append(q[0].MakeDepends, q[0].Depends...))
- return
- }
|