unified.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package download
  2. import (
  3. "context"
  4. "net/http"
  5. "os"
  6. "path/filepath"
  7. "sync"
  8. "github.com/leonelquinteros/gotext"
  9. "github.com/Jguer/yay/v11/pkg/db"
  10. "github.com/Jguer/yay/v11/pkg/multierror"
  11. "github.com/Jguer/yay/v11/pkg/settings/exe"
  12. "github.com/Jguer/yay/v11/pkg/settings/parser"
  13. "github.com/Jguer/yay/v11/pkg/text"
  14. )
  15. type httpRequestDoer interface {
  16. Get(string) (*http.Response, error)
  17. }
  18. type DBSearcher interface {
  19. SyncPackage(string) db.IPackage
  20. SatisfierFromDB(string, string) db.IPackage
  21. }
  22. func downloadGitRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder,
  23. pkgURL, pkgName, dest string, force bool, gitArgs ...string,
  24. ) (bool, error) {
  25. finalDir := filepath.Join(dest, pkgName)
  26. newClone := true
  27. switch _, err := os.Stat(filepath.Join(finalDir, ".git")); {
  28. case os.IsNotExist(err) || (err == nil && force):
  29. if _, errD := os.Stat(finalDir); force && errD == nil {
  30. if errR := os.RemoveAll(finalDir); errR != nil {
  31. return false, ErrGetPKGBUILDRepo{inner: errR, pkgName: pkgName, errOut: ""}
  32. }
  33. }
  34. gitArgs = append(gitArgs, pkgURL, pkgName)
  35. cloneArgs := make([]string, 0, len(gitArgs)+4)
  36. cloneArgs = append(cloneArgs, "clone", "--no-progress")
  37. cloneArgs = append(cloneArgs, gitArgs...)
  38. cmd := cmdBuilder.BuildGitCmd(ctx, dest, cloneArgs...)
  39. _, stderr, errCapture := cmdBuilder.Capture(cmd)
  40. if errCapture != nil {
  41. return false, ErrGetPKGBUILDRepo{inner: errCapture, pkgName: pkgName, errOut: stderr}
  42. }
  43. case err != nil:
  44. return false, ErrGetPKGBUILDRepo{
  45. inner: err,
  46. pkgName: pkgName,
  47. errOut: gotext.Get("error reading %s", filepath.Join(dest, pkgName, ".git")),
  48. }
  49. default:
  50. cmd := cmdBuilder.BuildGitCmd(ctx, filepath.Join(dest, pkgName), "pull", "--rebase", "--autostash")
  51. _, stderr, errCmd := cmdBuilder.Capture(cmd)
  52. if errCmd != nil {
  53. return false, ErrGetPKGBUILDRepo{inner: errCmd, pkgName: pkgName, errOut: stderr}
  54. }
  55. newClone = false
  56. }
  57. return newClone, nil
  58. }
  59. func getURLName(pkg db.IPackage) string {
  60. name := pkg.Base()
  61. if name == "" {
  62. name = pkg.Name()
  63. }
  64. return name
  65. }
  66. func PKGBUILDs(dbExecutor DBSearcher, httpClient *http.Client, targets []string,
  67. aurURL string, mode parser.TargetMode,
  68. ) (map[string][]byte, error) {
  69. pkgbuilds := make(map[string][]byte, len(targets))
  70. var (
  71. mux sync.Mutex
  72. errs multierror.MultiError
  73. wg sync.WaitGroup
  74. )
  75. sem := make(chan uint8, MaxConcurrentFetch)
  76. for _, target := range targets {
  77. // Probably replaceable by something in query.
  78. dbName, name, aur, toSkip := getPackageUsableName(dbExecutor, target, mode)
  79. if toSkip {
  80. continue
  81. }
  82. sem <- 1
  83. wg.Add(1)
  84. go func(target, dbName, pkgName string, aur bool) {
  85. var (
  86. err error
  87. pkgbuild []byte
  88. )
  89. if aur {
  90. pkgbuild, err = AURPKGBUILD(httpClient, pkgName, aurURL)
  91. } else {
  92. pkgbuild, err = ABSPKGBUILD(httpClient, dbName, pkgName)
  93. }
  94. if err == nil {
  95. mux.Lock()
  96. pkgbuilds[target] = pkgbuild
  97. mux.Unlock()
  98. } else {
  99. errs.Add(err)
  100. }
  101. <-sem
  102. wg.Done()
  103. }(target, dbName, name, aur)
  104. }
  105. wg.Wait()
  106. return pkgbuilds, errs.Return()
  107. }
  108. func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher,
  109. cmdBuilder exe.GitCmdBuilder,
  110. targets []string, mode parser.TargetMode, aurURL, dest string, force bool,
  111. ) (map[string]bool, error) {
  112. cloned := make(map[string]bool, len(targets))
  113. var (
  114. mux sync.Mutex
  115. errs multierror.MultiError
  116. wg sync.WaitGroup
  117. )
  118. sem := make(chan uint8, MaxConcurrentFetch)
  119. for _, target := range targets {
  120. // Probably replaceable by something in query.
  121. dbName, name, aur, toSkip := getPackageUsableName(dbExecutor, target, mode)
  122. if toSkip {
  123. continue
  124. }
  125. sem <- 1
  126. wg.Add(1)
  127. go func(target, dbName, pkgName string, aur bool) {
  128. var (
  129. err error
  130. newClone bool
  131. )
  132. if aur {
  133. newClone, err = AURPKGBUILDRepo(ctx, cmdBuilder, aurURL, pkgName, dest, force)
  134. } else {
  135. newClone, err = ABSPKGBUILDRepo(ctx, cmdBuilder, dbName, pkgName, dest, force)
  136. }
  137. progress := 0
  138. if err != nil {
  139. errs.Add(err)
  140. } else {
  141. mux.Lock()
  142. cloned[target] = newClone
  143. progress = len(cloned)
  144. mux.Unlock()
  145. }
  146. if aur {
  147. text.OperationInfoln(
  148. gotext.Get("(%d/%d) Downloaded PKGBUILD: %s",
  149. progress, len(targets), text.Cyan(pkgName)))
  150. } else {
  151. text.OperationInfoln(
  152. gotext.Get("(%d/%d) Downloaded PKGBUILD from ABS: %s",
  153. progress, len(targets), text.Cyan(pkgName)))
  154. }
  155. <-sem
  156. wg.Done()
  157. }(target, dbName, name, aur)
  158. }
  159. wg.Wait()
  160. return cloned, errs.Return()
  161. }
  162. // TODO: replace with dep.ResolveTargets.
  163. func getPackageUsableName(dbExecutor DBSearcher, target string, mode parser.TargetMode) (dbname, pkgname string, aur, toSkip bool) {
  164. aur = true
  165. dbName, name := text.SplitDBFromName(target)
  166. if dbName != "aur" && mode.AtLeastRepo() {
  167. var pkg db.IPackage
  168. if dbName != "" {
  169. pkg = dbExecutor.SatisfierFromDB(name, dbName)
  170. if pkg == nil {
  171. // if the user precised a db but the package is not in the db
  172. // then it is missing
  173. // Mode does not allow AUR packages
  174. return dbName, name, aur, true
  175. }
  176. } else {
  177. pkg = dbExecutor.SyncPackage(name)
  178. }
  179. if pkg != nil {
  180. aur = false
  181. name = getURLName(pkg)
  182. dbName = pkg.DB().Name()
  183. }
  184. }
  185. if aur && mode == parser.ModeRepo {
  186. return dbName, name, aur, true
  187. }
  188. return dbName, name, aur, false
  189. }