clean.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "github.com/leonelquinteros/gotext"
  8. "github.com/Jguer/yay/v11/pkg/db"
  9. "github.com/Jguer/yay/v11/pkg/dep"
  10. "github.com/Jguer/yay/v11/pkg/query"
  11. "github.com/Jguer/yay/v11/pkg/settings"
  12. "github.com/Jguer/yay/v11/pkg/settings/parser"
  13. "github.com/Jguer/yay/v11/pkg/stringset"
  14. "github.com/Jguer/yay/v11/pkg/text"
  15. )
  16. // CleanDependencies removes all dangling dependencies in system.
  17. func cleanDependencies(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor, removeOptional bool) error {
  18. hanging := hangingPackages(removeOptional, dbExecutor)
  19. if len(hanging) != 0 {
  20. return cleanRemove(ctx, cmdArgs, hanging)
  21. }
  22. return nil
  23. }
  24. // CleanRemove sends a full removal command to pacman with the pkgName slice.
  25. func cleanRemove(ctx context.Context, cmdArgs *parser.Arguments, pkgNames []string) error {
  26. if len(pkgNames) == 0 {
  27. return nil
  28. }
  29. arguments := cmdArgs.CopyGlobal()
  30. _ = arguments.AddArg("R")
  31. arguments.AddTarget(pkgNames...)
  32. return config.Runtime.CmdBuilder.Show(
  33. config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
  34. arguments, config.Runtime.Mode, settings.NoConfirm))
  35. }
  36. func syncClean(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
  37. keepInstalled := false
  38. keepCurrent := false
  39. _, removeAll, _ := cmdArgs.GetArg("c", "clean")
  40. for _, v := range config.Runtime.PacmanConf.CleanMethod {
  41. if v == "KeepInstalled" {
  42. keepInstalled = true
  43. } else if v == "KeepCurrent" {
  44. keepCurrent = true
  45. }
  46. }
  47. if config.Runtime.Mode.AtLeastRepo() {
  48. if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
  49. cmdArgs, config.Runtime.Mode, settings.NoConfirm)); err != nil {
  50. return err
  51. }
  52. }
  53. if !config.Runtime.Mode.AtLeastAUR() {
  54. return nil
  55. }
  56. var question string
  57. if removeAll {
  58. question = gotext.Get("Do you want to remove ALL AUR packages from cache?")
  59. } else {
  60. question = gotext.Get("Do you want to remove all other AUR packages from cache?")
  61. }
  62. fmt.Println(gotext.Get("\nBuild directory:"), config.BuildDir)
  63. if text.ContinueTask(question, true, settings.NoConfirm) {
  64. if err := cleanAUR(ctx, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil {
  65. return err
  66. }
  67. }
  68. if removeAll {
  69. return nil
  70. }
  71. if text.ContinueTask(gotext.Get("Do you want to remove ALL untracked AUR files?"), true, settings.NoConfirm) {
  72. return cleanUntracked(ctx)
  73. }
  74. return nil
  75. }
  76. func cleanAUR(ctx context.Context, keepInstalled, keepCurrent, removeAll bool, dbExecutor db.Executor) error {
  77. fmt.Println(gotext.Get("removing AUR packages from cache..."))
  78. installedBases := make(stringset.StringSet)
  79. inAURBases := make(stringset.StringSet)
  80. remotePackages, _ := query.GetRemotePackages(dbExecutor)
  81. files, err := os.ReadDir(config.BuildDir)
  82. if err != nil {
  83. return err
  84. }
  85. cachedPackages := make([]string, 0, len(files))
  86. for _, file := range files {
  87. if !file.IsDir() {
  88. continue
  89. }
  90. cachedPackages = append(cachedPackages, file.Name())
  91. }
  92. // Most people probably don't use keep current and that is the only
  93. // case where this is needed.
  94. // Querying the AUR is slow and needs internet so don't do it if we
  95. // don't need to.
  96. if keepCurrent {
  97. info, errInfo := query.AURInfo(ctx, config.Runtime.AURClient, cachedPackages, &query.AURWarnings{}, config.RequestSplitN)
  98. if errInfo != nil {
  99. return errInfo
  100. }
  101. for _, pkg := range info {
  102. inAURBases.Set(pkg.PackageBase)
  103. }
  104. }
  105. for _, pkg := range remotePackages {
  106. if pkg.Base() != "" {
  107. installedBases.Set(pkg.Base())
  108. } else {
  109. installedBases.Set(pkg.Name())
  110. }
  111. }
  112. for _, file := range files {
  113. if !file.IsDir() {
  114. continue
  115. }
  116. if !removeAll {
  117. if keepInstalled && installedBases.Get(file.Name()) {
  118. continue
  119. }
  120. if keepCurrent && inAURBases.Get(file.Name()) {
  121. continue
  122. }
  123. }
  124. err = os.RemoveAll(filepath.Join(config.BuildDir, file.Name()))
  125. if err != nil {
  126. return nil
  127. }
  128. }
  129. return nil
  130. }
  131. func cleanUntracked(ctx context.Context) error {
  132. fmt.Println(gotext.Get("removing untracked AUR files from cache..."))
  133. files, err := os.ReadDir(config.BuildDir)
  134. if err != nil {
  135. return err
  136. }
  137. for _, file := range files {
  138. if !file.IsDir() {
  139. continue
  140. }
  141. dir := filepath.Join(config.BuildDir, file.Name())
  142. if isGitRepository(dir) {
  143. if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fx")); err != nil {
  144. text.Warnln(gotext.Get("Unable to clean:"), dir)
  145. return err
  146. }
  147. }
  148. }
  149. return nil
  150. }
  151. func isGitRepository(dir string) bool {
  152. _, err := os.Stat(filepath.Join(dir, ".git"))
  153. return !os.IsNotExist(err)
  154. }
  155. func cleanAfter(ctx context.Context, bases []dep.Base) {
  156. fmt.Println(gotext.Get("removing untracked AUR files from cache..."))
  157. for i, base := range bases {
  158. dir := filepath.Join(config.BuildDir, base.Pkgbase())
  159. if !isGitRepository(dir) {
  160. continue
  161. }
  162. text.OperationInfoln(gotext.Get("Cleaning (%d/%d): %s", i+1, len(bases), text.Cyan(dir)))
  163. _, stderr, err := config.Runtime.CmdBuilder.Capture(
  164. config.Runtime.CmdBuilder.BuildGitCmd(
  165. ctx, dir, "reset", "--hard", "HEAD"))
  166. if err != nil {
  167. text.Errorln(gotext.Get("error resetting %s: %s", base.String(), stderr))
  168. }
  169. if err := config.Runtime.CmdBuilder.Show(
  170. config.Runtime.CmdBuilder.BuildGitCmd(
  171. ctx, "clean", "-fx", "--exclude='*.pkg.*'")); err != nil {
  172. fmt.Fprintln(os.Stderr, err)
  173. }
  174. }
  175. }
  176. func cleanBuilds(bases []dep.Base) {
  177. for i, base := range bases {
  178. dir := filepath.Join(config.BuildDir, base.Pkgbase())
  179. text.OperationInfoln(gotext.Get("Deleting (%d/%d): %s", i+1, len(bases), text.Cyan(dir)))
  180. if err := os.RemoveAll(dir); err != nil {
  181. fmt.Fprintln(os.Stderr, err)
  182. }
  183. }
  184. }