diff_menu.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // file dedicated to diff menu
  2. package menus
  3. import (
  4. "context"
  5. "fmt"
  6. "io"
  7. "os"
  8. "strings"
  9. mapset "github.com/deckarep/golang-set/v2"
  10. "github.com/leonelquinteros/gotext"
  11. "github.com/Jguer/yay/v12/pkg/multierror"
  12. "github.com/Jguer/yay/v12/pkg/settings"
  13. "github.com/Jguer/yay/v12/pkg/settings/exe"
  14. "github.com/Jguer/yay/v12/pkg/text"
  15. )
  16. const (
  17. gitEmptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
  18. gitDiffRefName = "AUR_SEEN"
  19. )
  20. func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder,
  21. pkgbuildDirs map[string]string, bases []string,
  22. ) error {
  23. var errMulti multierror.MultiError
  24. for _, pkg := range bases {
  25. dir := pkgbuildDirs[pkg]
  26. start, err := getLastSeenHash(ctx, cmdBuilder, dir)
  27. if err != nil {
  28. errMulti.Add(err)
  29. continue
  30. }
  31. if start != gitEmptyTree {
  32. hasDiff, err := gitHasDiff(ctx, cmdBuilder, dir)
  33. if err != nil {
  34. errMulti.Add(err)
  35. continue
  36. }
  37. if !hasDiff {
  38. text.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(pkg)))
  39. continue
  40. }
  41. }
  42. args := []string{
  43. "diff",
  44. start + "..HEAD@{upstream}", "--src-prefix",
  45. dir + "/", "--dst-prefix", dir + "/", "--", ".", ":(exclude).SRCINFO",
  46. }
  47. if text.UseColor {
  48. args = append(args, "--color=always")
  49. } else {
  50. args = append(args, "--color=never")
  51. }
  52. _ = cmdBuilder.Show(cmdBuilder.BuildGitCmd(ctx, dir, args...))
  53. }
  54. return errMulti.Return()
  55. }
  56. // Check whether or not a diff exists between the last reviewed diff and
  57. // HEAD@{upstream}.
  58. func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (bool, error) {
  59. if gitHasLastSeenRef(ctx, cmdBuilder, dir) {
  60. stdout, stderr, err := cmdBuilder.Capture(
  61. cmdBuilder.BuildGitCmd(ctx, dir, "rev-parse", gitDiffRefName, "HEAD@{upstream}"))
  62. if err != nil {
  63. return false, fmt.Errorf("%s%w", stderr, err)
  64. }
  65. lines := strings.Split(stdout, "\n")
  66. lastseen := lines[0]
  67. upstream := lines[1]
  68. return lastseen != upstream, nil
  69. }
  70. // If YAY_DIFF_REVIEW does not exists, we have never reviewed a diff for this package
  71. // and should display it.
  72. return true, nil
  73. }
  74. // Return wether or not we have reviewed a diff yet. It checks for the existence of
  75. // YAY_DIFF_REVIEW in the git ref-list.
  76. func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) bool {
  77. _, _, err := cmdBuilder.Capture(
  78. cmdBuilder.BuildGitCmd(ctx,
  79. dir, "rev-parse", "--quiet", "--verify", gitDiffRefName))
  80. return err == nil
  81. }
  82. // Returns the last reviewed hash. If YAY_DIFF_REVIEW exists it will return this hash.
  83. // If it does not it will return empty tree as no diff have been reviewed yet.
  84. func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (string, error) {
  85. if gitHasLastSeenRef(ctx, cmdBuilder, dir) {
  86. stdout, stderr, err := cmdBuilder.Capture(
  87. cmdBuilder.BuildGitCmd(ctx,
  88. dir, "rev-parse", gitDiffRefName))
  89. if err != nil {
  90. return "", fmt.Errorf("%s %w", stderr, err)
  91. }
  92. lines := strings.Split(stdout, "\n")
  93. return lines[0], nil
  94. }
  95. return gitEmptyTree, nil
  96. }
  97. // Update the YAY_DIFF_REVIEW ref to HEAD. We use this ref to determine which diff were
  98. // reviewed by the user.
  99. func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error {
  100. _, stderr, err := cmdBuilder.Capture(
  101. cmdBuilder.BuildGitCmd(ctx,
  102. dir, "update-ref", gitDiffRefName, "HEAD"))
  103. if err != nil {
  104. return fmt.Errorf("%s %w", stderr, err)
  105. }
  106. return nil
  107. }
  108. func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string, bases []string) error {
  109. var errMulti multierror.MultiError
  110. for _, pkg := range bases {
  111. dir := pkgbuildDirs[pkg]
  112. if err := gitUpdateSeenRef(ctx, cmdBuilder, dir); err != nil {
  113. errMulti.Add(err)
  114. }
  115. }
  116. return errMulti.Return()
  117. }
  118. func Diff(ctx context.Context, cmdBuilder exe.ICmdBuilder, w io.Writer,
  119. pkgbuildDirs map[string]string, diffMenuOption bool,
  120. installed mapset.Set[string], cloned map[string]bool, noConfirm bool, diffDefaultAnswer string,
  121. ) error {
  122. if !diffMenuOption {
  123. return nil
  124. }
  125. bases := make([]string, 0, len(pkgbuildDirs))
  126. for base := range pkgbuildDirs {
  127. bases = append(bases, base)
  128. }
  129. toDiff, errMenu := selectionMenu(w, pkgbuildDirs, bases, installed, gotext.Get("Diffs to show?"),
  130. noConfirm, diffDefaultAnswer, nil)
  131. if errMenu != nil || len(toDiff) == 0 {
  132. return errMenu
  133. }
  134. if errD := showPkgbuildDiffs(ctx, cmdBuilder, pkgbuildDirs, toDiff); errD != nil {
  135. return errD
  136. }
  137. fmt.Println()
  138. if !text.ContinueTask(os.Stdin, gotext.Get("Proceed with install?"), true, false) {
  139. return settings.ErrUserAbort{}
  140. }
  141. if errUpd := updatePkgbuildSeenRef(ctx, cmdBuilder, pkgbuildDirs, toDiff); errUpd != nil {
  142. return errUpd
  143. }
  144. return nil
  145. }
  146. func DiffFn(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error {
  147. if len(pkgbuildDirsByBase) == 0 {
  148. return nil // no work to do
  149. }
  150. bases := make([]string, 0, len(pkgbuildDirsByBase))
  151. for base := range pkgbuildDirsByBase {
  152. bases = append(bases, base)
  153. }
  154. toDiff, errMenu := selectionMenu(w, pkgbuildDirsByBase, bases, mapset.NewThreadUnsafeSet[string](), gotext.Get("Diffs to show?"),
  155. settings.NoConfirm, config.AnswerDiff, nil)
  156. if errMenu != nil || len(toDiff) == 0 {
  157. return errMenu
  158. }
  159. if errD := showPkgbuildDiffs(ctx, config.Runtime.CmdBuilder, pkgbuildDirsByBase, toDiff); errD != nil {
  160. return errD
  161. }
  162. fmt.Println()
  163. if !text.ContinueTask(os.Stdin, gotext.Get("Proceed with install?"), true, false) {
  164. return settings.ErrUserAbort{}
  165. }
  166. if errUpd := updatePkgbuildSeenRef(ctx, config.Runtime.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil {
  167. return errUpd
  168. }
  169. return nil
  170. }