diff_menu.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // file dedicated to diff menu
  2. package menus
  3. import (
  4. "context"
  5. "fmt"
  6. "io"
  7. "strings"
  8. mapset "github.com/deckarep/golang-set/v2"
  9. "github.com/leonelquinteros/gotext"
  10. "github.com/Jguer/yay/v12/pkg/multierror"
  11. "github.com/Jguer/yay/v12/pkg/runtime"
  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, logger *text.Logger,
  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. logger.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 whether 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 DiffFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
  119. pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
  120. ) error {
  121. if len(pkgbuildDirsByBase) == 0 {
  122. return nil // no work to do
  123. }
  124. bases := make([]string, 0, len(pkgbuildDirsByBase))
  125. for base := range pkgbuildDirsByBase {
  126. bases = append(bases, base)
  127. }
  128. toDiff, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, gotext.Get("Diffs to show?"),
  129. settings.NoConfirm, run.Cfg.AnswerDiff, nil)
  130. if errMenu != nil || len(toDiff) == 0 {
  131. return errMenu
  132. }
  133. if errD := showPkgbuildDiffs(ctx, run.CmdBuilder, run.Logger, pkgbuildDirsByBase, toDiff); errD != nil {
  134. return errD
  135. }
  136. run.Logger.Println()
  137. if !run.Logger.ContinueTask(gotext.Get("Proceed with install?"), true, false) {
  138. return settings.ErrUserAbort{}
  139. }
  140. if errUpd := updatePkgbuildSeenRef(ctx, run.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil {
  141. return errUpd
  142. }
  143. return nil
  144. }