You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

pull.go 5.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package pull
  5. import (
  6. "context"
  7. "fmt"
  8. "os"
  9. "path"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/git"
  12. "code.gitea.io/gitea/modules/graceful"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/notification"
  15. issue_service "code.gitea.io/gitea/services/issue"
  16. )
  17. // NewPullRequest creates new pull request with labels for repository.
  18. func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int64, uuids []string, pr *models.PullRequest, assigneeIDs []int64) error {
  19. if err := TestPatch(pr); err != nil {
  20. return err
  21. }
  22. if err := models.NewPullRequest(repo, pull, labelIDs, uuids, pr); err != nil {
  23. return err
  24. }
  25. for _, assigneeID := range assigneeIDs {
  26. if err := issue_service.AddAssigneeIfNotAssigned(pull, pull.Poster, assigneeID); err != nil {
  27. return err
  28. }
  29. }
  30. pr.Issue = pull
  31. pull.PullRequest = pr
  32. if err := PushToBaseRepo(pr); err != nil {
  33. return err
  34. }
  35. notification.NotifyNewPullRequest(pr)
  36. return nil
  37. }
  38. func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *models.User, branch string) error {
  39. repo, err := models.GetRepositoryByID(repoID)
  40. if err != nil {
  41. return fmt.Errorf("GetRepositoryByID: %v", err)
  42. }
  43. gitRepo, err := git.OpenRepository(repo.RepoPath())
  44. if err != nil {
  45. return fmt.Errorf("git.OpenRepository: %v", err)
  46. }
  47. go func() {
  48. // FIXME: graceful: We need to tell the manager we're doing something...
  49. err := requests.InvalidateCodeComments(doer, gitRepo, branch)
  50. if err != nil {
  51. log.Error("PullRequestList.InvalidateCodeComments: %v", err)
  52. }
  53. gitRepo.Close()
  54. }()
  55. return nil
  56. }
  57. func addHeadRepoTasks(prs []*models.PullRequest) {
  58. for _, pr := range prs {
  59. log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID)
  60. if err := PushToBaseRepo(pr); err != nil {
  61. log.Error("PushToBaseRepo: %v", err)
  62. continue
  63. }
  64. AddToTaskQueue(pr)
  65. }
  66. }
  67. // AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
  68. // and generate new patch for testing as needed.
  69. func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSync bool) {
  70. log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
  71. graceful.GetManager().RunWithShutdownContext(func(ctx context.Context) {
  72. // There is no sensible way to shut this down ":-("
  73. // If you don't let it run all the way then you will lose data
  74. // FIXME: graceful: AddTestPullRequestTask needs to become a queue!
  75. prs, err := models.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
  76. if err != nil {
  77. log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
  78. return
  79. }
  80. if isSync {
  81. requests := models.PullRequestList(prs)
  82. if err = requests.LoadAttributes(); err != nil {
  83. log.Error("PullRequestList.LoadAttributes: %v", err)
  84. }
  85. if invalidationErr := checkForInvalidation(requests, repoID, doer, branch); invalidationErr != nil {
  86. log.Error("checkForInvalidation: %v", invalidationErr)
  87. }
  88. if err == nil {
  89. for _, pr := range prs {
  90. pr.Issue.PullRequest = pr
  91. notification.NotifyPullRequestSynchronized(doer, pr)
  92. }
  93. }
  94. }
  95. addHeadRepoTasks(prs)
  96. log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
  97. prs, err = models.GetUnmergedPullRequestsByBaseInfo(repoID, branch)
  98. if err != nil {
  99. log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
  100. return
  101. }
  102. for _, pr := range prs {
  103. AddToTaskQueue(pr)
  104. }
  105. })
  106. }
  107. // PushToBaseRepo pushes commits from branches of head repository to
  108. // corresponding branches of base repository.
  109. // FIXME: Only push branches that are actually updates?
  110. func PushToBaseRepo(pr *models.PullRequest) (err error) {
  111. log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName())
  112. headRepoPath := pr.HeadRepo.RepoPath()
  113. headGitRepo, err := git.OpenRepository(headRepoPath)
  114. if err != nil {
  115. return fmt.Errorf("OpenRepository: %v", err)
  116. }
  117. defer headGitRepo.Close()
  118. tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID)
  119. if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil {
  120. return fmt.Errorf("headGitRepo.AddRemote: %v", err)
  121. }
  122. // Make sure to remove the remote even if the push fails
  123. defer func() {
  124. if err := headGitRepo.RemoveRemote(tmpRemoteName); err != nil {
  125. log.Error("PushToBaseRepo: RemoveRemote: %s", err)
  126. }
  127. }()
  128. headFile := pr.GetGitRefName()
  129. // Remove head in case there is a conflict.
  130. file := path.Join(pr.BaseRepo.RepoPath(), headFile)
  131. _ = os.Remove(file)
  132. if err = git.Push(headRepoPath, git.PushOptions{
  133. Remote: tmpRemoteName,
  134. Branch: fmt.Sprintf("%s:%s", pr.HeadBranch, headFile),
  135. Force: true,
  136. }); err != nil {
  137. return fmt.Errorf("Push: %v", err)
  138. }
  139. return nil
  140. }