diff --git a/.golangci.yml b/.golangci.yml index afba2954..750cd137 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -95,6 +95,7 @@ issues: - gomnd exclude: + - G107 - G204 - commentedOutCode diff --git a/cmd.go b/cmd.go index c2a7422f..89e83db1 100644 --- a/cmd.go +++ b/cmd.go @@ -240,8 +240,7 @@ func handleYay(cmdArgs *settings.Arguments, dbExecutor db.Executor) error { } func handleGetpkgbuild(cmdArgs *settings.Arguments, dbExecutor db.Executor) error { - switch { - case cmdArgs.ExistsArg("p", "pkgbuild"): + if cmdArgs.ExistsArg("p", "pkgbuild") { return printPkgbuilds(dbExecutor, cmdArgs.Targets) } return getPkgbuilds(cmdArgs.Targets, dbExecutor, cmdArgs.ExistsArg("f", "force")) diff --git a/pkg/download/abs.go b/pkg/download/abs.go new file mode 100644 index 00000000..30b78aa1 --- /dev/null +++ b/pkg/download/abs.go @@ -0,0 +1,49 @@ +package download + +import ( + "errors" + "io/ioutil" + "net/http" + "net/url" +) + +var ErrInvalidRepository = errors.New("invalid repository") +var ErrABSPackageNotFound = errors.New("package not found in repos") + +const MaxConcurrentFetch = 20 +const ABSPackageURL = "https://git.archlinux.org/svntogit/packages.git/plain/trunk/PKGBUILD?" +const ABSCommunityURL = "https://git.archlinux.org/svntogit/community.git/plain/trunk/PKGBUILD?" + +func getPackageURL(db, pkgName string) (string, error) { + values := url.Values{} + values.Set("h", "packages/"+pkgName) + nameEncoded := values.Encode() + switch db { + case "core", "extra", "testing": + return ABSPackageURL + nameEncoded, nil + case "community", "multilib", "community-testing", "multilib-testing": + return ABSCommunityURL + nameEncoded, nil + } + return "", ErrInvalidRepository +} + +func GetABSPkgbuild(dbName, pkgName string) ([]byte, error) { + packageURL, err := getPackageURL(dbName, pkgName) + if err != nil { + return nil, err + } + + resp, err := http.Get(packageURL) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + pkgBuild, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return pkgBuild, nil +} diff --git a/pkg/download/aur.go b/pkg/download/aur.go new file mode 100644 index 00000000..85aab17b --- /dev/null +++ b/pkg/download/aur.go @@ -0,0 +1,35 @@ +package download + +import ( + "errors" + "io/ioutil" + "net/http" + "net/url" +) + +const AURPackageURL = "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?" + +var ErrAURPackageNotFound = errors.New("package not found in AUR") + +func GetAURPkgbuild(pkgName string) ([]byte, error) { + values := url.Values{} + values.Set("h", pkgName) + pkgURL := AURPackageURL + values.Encode() + + resp, err := http.Get(pkgURL) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, ErrAURPackageNotFound + } + + defer resp.Body.Close() + + pkgBuild, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return pkgBuild, nil +} diff --git a/pkg/download/unified.go b/pkg/download/unified.go new file mode 100644 index 00000000..880f2db4 --- /dev/null +++ b/pkg/download/unified.go @@ -0,0 +1,74 @@ +package download + +import ( + "sync" + + "github.com/Jguer/go-alpm/v2" + + "github.com/Jguer/yay/v10/pkg/db" + "github.com/Jguer/yay/v10/pkg/multierror" + "github.com/Jguer/yay/v10/pkg/settings" + "github.com/Jguer/yay/v10/pkg/text" +) + +func getURLName(pkg alpm.IPackage) string { + name := pkg.Base() + if name == "" { + name = pkg.Name() + } + return name +} + +func GetPkgbuilds(dbExecutor db.Executor, targets []string, mode settings.TargetMode) (map[string][]byte, error) { + pkgbuilds := make(map[string][]byte, len(targets)) + var mux sync.Mutex + var errs multierror.MultiError + var wg sync.WaitGroup + sem := make(chan uint8, MaxConcurrentFetch) + + for _, target := range targets { + aur := true + dbName, name := text.SplitDBFromName(target) + if dbName != "aur" && (mode == settings.ModeAny || mode == settings.ModeRepo) { + pkg := dbExecutor.SyncPackage(name) + if pkg != nil { + aur = false + name = getURLName(pkg) + dbName = pkg.DB().Name() + } + } + + if aur && mode == settings.ModeRepo { + // Mode does not allow AUR packages + continue + } + + sem <- 1 + wg.Add(1) + + go func(target, dbName, pkgName string, aur bool) { + var err error + var pkgbuild []byte + + if aur { + pkgbuild, err = GetAURPkgbuild(pkgName) + } else { + pkgbuild, err = GetABSPkgbuild(dbName, pkgName) + } + + if err == nil { + mux.Lock() + pkgbuilds[target] = pkgbuild + mux.Unlock() + } else { + errs.Add(err) + } + + <-sem + wg.Done() + }(target, dbName, name, aur) + } + + wg.Wait() + return pkgbuilds, errs.Return() +} diff --git a/print.go b/print.go index 2ac27b6c..5629003b 100644 --- a/print.go +++ b/print.go @@ -4,12 +4,13 @@ import ( "fmt" "os" "strconv" + "strings" "github.com/leonelquinteros/gotext" rpc "github.com/mikkeloscar/aur" "github.com/Jguer/yay/v10/pkg/db" - "github.com/Jguer/yay/v10/pkg/multierror" + "github.com/Jguer/yay/v10/pkg/download" "github.com/Jguer/yay/v10/pkg/query" "github.com/Jguer/yay/v10/pkg/settings" "github.com/Jguer/yay/v10/pkg/stringset" @@ -268,45 +269,28 @@ outer: return nil } -func printPkgbuilds(dbExecutor db.Executor, pkgS []string) error { - var pkgbuilds []string - var localPkgbuilds []string - missing := false - pkgS = query.RemoveInvalidTargets(pkgS, config.Runtime.Mode) - aurS, repoS := packageSlices(pkgS, dbExecutor) - var err error - var errs multierror.MultiError - - if len(aurS) != 0 { - noDB := make([]string, 0, len(aurS)) - for _, pkg := range aurS { - _, name := text.SplitDBFromName(pkg) - noDB = append(noDB, name) - } - localPkgbuilds, err = aurPkgbuilds(noDB) - pkgbuilds = append(pkgbuilds, localPkgbuilds...) - errs.Add(err) - } - - if len(repoS) != 0 { - localPkgbuilds, err = repoPkgbuilds(dbExecutor, repoS) - pkgbuilds = append(pkgbuilds, localPkgbuilds...) - errs.Add(err) - } - - if len(aurS)+len(repoS) != len(pkgbuilds) { - missing = true +func printPkgbuilds(dbExecutor db.Executor, targets []string) error { + pkgbuilds, err := download.GetPkgbuilds(dbExecutor, targets, config.Runtime.Mode) + if err != nil { + text.Errorln(err) } if len(pkgbuilds) != 0 { - for _, pkgbuild := range pkgbuilds { - fmt.Print(pkgbuild) + for target, pkgbuild := range pkgbuilds { + fmt.Printf("\n\n# %s\n\n", target) + fmt.Print(string(pkgbuild)) } } - if missing { - err = fmt.Errorf("Missing packages") + if len(pkgbuilds) != len(targets) { + missing := []string{} + for _, target := range targets { + if _, ok := pkgbuilds[target]; !ok { + missing = append(missing, target) + } + } + text.Warnln("Unable to find the following packages:", strings.Join(missing, ", ")) } - return err + return nil } diff --git a/query.go b/query.go index a93f3d2a..7d9b3917 100644 --- a/query.go +++ b/query.go @@ -3,21 +3,15 @@ package main import ( "errors" "fmt" - "io/ioutil" - "net/http" - "net/url" "os" "sort" "strings" - "sync" alpm "github.com/Jguer/go-alpm/v2" "github.com/leonelquinteros/gotext" rpc "github.com/mikkeloscar/aur" "github.com/Jguer/yay/v10/pkg/db" - "github.com/Jguer/yay/v10/pkg/intrange" - "github.com/Jguer/yay/v10/pkg/multierror" "github.com/Jguer/yay/v10/pkg/query" "github.com/Jguer/yay/v10/pkg/settings" "github.com/Jguer/yay/v10/pkg/stringset" @@ -259,7 +253,6 @@ func queryRepo(pkgInputN []string, dbExecutor db.Executor) repoQuery { func packageSlices(toCheck []string, dbExecutor db.Executor) (aur, repo []string) { for _, _pkg := range toCheck { dbName, name := text.SplitDBFromName(_pkg) - found := false if dbName == "aur" || config.Runtime.Mode == settings.ModeAUR { aur = append(aur, _pkg) @@ -269,13 +262,8 @@ func packageSlices(toCheck []string, dbExecutor db.Executor) (aur, repo []string continue } - found = dbExecutor.SyncSatisfierExists(name) - - if !found { - found = len(dbExecutor.PackagesFromGroup(name)) != 0 - } - - if found { + if dbExecutor.SyncSatisfierExists(name) || + len(dbExecutor.PackagesFromGroup(name)) != 0 { repo = append(repo, _pkg) } else { aur = append(aur, _pkg) @@ -390,127 +378,3 @@ func statistics(dbExecutor db.Executor) *struct { return info } - -func aurPkgbuilds(names []string) ([]string, error) { - pkgbuilds := make([]string, 0, len(names)) - var mux sync.Mutex - var wg sync.WaitGroup - var errs multierror.MultiError - - makeRequest := func(n, max int) { - defer wg.Done() - - for _, name := range names[n:max] { - values := url.Values{} - values.Set("h", name) - - url := "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?" - - resp, err := http.Get(url + values.Encode()) - if err != nil { - errs.Add(err) - continue - } - if resp.StatusCode != 200 { - errs.Add(fmt.Errorf("error code %d for package %s", resp.StatusCode, name)) - continue - } - defer resp.Body.Close() - - body, readErr := ioutil.ReadAll(resp.Body) - pkgbuild := string(body) - - if readErr != nil { - errs.Add(readErr) - continue - } - - mux.Lock() - pkgbuilds = append(pkgbuilds, pkgbuild) - mux.Unlock() - } - } - - for n := 0; n < len(names); n += 20 { - max := intrange.Min(len(names), n+20) - wg.Add(1) - go makeRequest(n, max) - } - - wg.Wait() - - if err := errs.Return(); err != nil { - return pkgbuilds, err - } - - return pkgbuilds, nil -} - -func repoPkgbuilds(dbExecutor db.Executor, names []string) ([]string, error) { - pkgbuilds := make([]string, 0, len(names)) - var mux sync.Mutex - var wg sync.WaitGroup - var errs multierror.MultiError - - makeRequest := func(full string) { - var pkg alpm.IPackage - defer wg.Done() - - db, name := text.SplitDBFromName(full) - - if db != "" { - pkg = dbExecutor.SatisfierFromDB(name, db) - } else { - pkg = dbExecutor.SyncSatisfier(name) - } - - values := url.Values{} - values.Set("h", "packages/"+name) - - var url string - - // TODO: Check existence with ls-remote - // https://git.archlinux.org/svntogit/packages.git - switch pkg.DB().Name() { - case "core", "extra", "testing": - url = "https://git.archlinux.org/svntogit/packages.git/plain/trunk/PKGBUILD?" - case "community", "multilib", "community-testing", "multilib-testing": - url = "https://git.archlinux.org/svntogit/community.git/plain/trunk/PKGBUILD?" - default: - errs.Add(fmt.Errorf("unable to get PKGBUILD from repo \"%s\"", db)) - return - } - - resp, err := http.Get(url + values.Encode()) - if err != nil { - errs.Add(err) - return - } - if resp.StatusCode != 200 { - errs.Add(fmt.Errorf("error code %d for package %s", resp.StatusCode, name)) - return - } - defer resp.Body.Close() - - body, readErr := ioutil.ReadAll(resp.Body) - pkgbuild := string(body) - - if readErr != nil { - errs.Add(readErr) - return - } - - mux.Lock() - pkgbuilds = append(pkgbuilds, pkgbuild) - mux.Unlock() - } - - for _, full := range names { - wg.Add(1) - go makeRequest(full) - } - - wg.Wait() - - return pkgbuilds, errs.Return() -}