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

commit_status.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright 2019 The Gitea Authors.
  2. // All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package pull
  6. import (
  7. "code.gitea.io/gitea/models"
  8. "code.gitea.io/gitea/modules/git"
  9. "code.gitea.io/gitea/modules/structs"
  10. "github.com/pkg/errors"
  11. )
  12. // MergeRequiredContextsCommitStatus returns a commit status state for given required contexts
  13. func MergeRequiredContextsCommitStatus(commitStatuses []*models.CommitStatus, requiredContexts []string) structs.CommitStatusState {
  14. if len(requiredContexts) == 0 {
  15. status := models.CalcCommitStatus(commitStatuses)
  16. if status != nil {
  17. return status.State
  18. }
  19. return structs.CommitStatusSuccess
  20. }
  21. var returnedStatus = structs.CommitStatusSuccess
  22. for _, ctx := range requiredContexts {
  23. var targetStatus structs.CommitStatusState
  24. for _, commitStatus := range commitStatuses {
  25. if commitStatus.Context == ctx {
  26. targetStatus = commitStatus.State
  27. break
  28. }
  29. }
  30. if targetStatus == "" {
  31. targetStatus = structs.CommitStatusPending
  32. commitStatuses = append(commitStatuses, &models.CommitStatus{
  33. State: targetStatus,
  34. Context: ctx,
  35. Description: "Pending",
  36. })
  37. }
  38. if targetStatus.NoBetterThan(returnedStatus) {
  39. returnedStatus = targetStatus
  40. }
  41. }
  42. return returnedStatus
  43. }
  44. // IsCommitStatusContextSuccess returns true if all required status check contexts succeed.
  45. func IsCommitStatusContextSuccess(commitStatuses []*models.CommitStatus, requiredContexts []string) bool {
  46. // If no specific context is required, require that last commit status is a success
  47. if len(requiredContexts) == 0 {
  48. status := models.CalcCommitStatus(commitStatuses)
  49. if status == nil || status.State != structs.CommitStatusSuccess {
  50. return false
  51. }
  52. return true
  53. }
  54. for _, ctx := range requiredContexts {
  55. var found bool
  56. for _, commitStatus := range commitStatuses {
  57. if commitStatus.Context == ctx {
  58. if commitStatus.State != structs.CommitStatusSuccess {
  59. return false
  60. }
  61. found = true
  62. break
  63. }
  64. }
  65. if !found {
  66. return false
  67. }
  68. }
  69. return true
  70. }
  71. // IsPullCommitStatusPass returns if all required status checks PASS
  72. func IsPullCommitStatusPass(pr *models.PullRequest) (bool, error) {
  73. if err := pr.LoadProtectedBranch(); err != nil {
  74. return false, errors.Wrap(err, "GetLatestCommitStatus")
  75. }
  76. if pr.ProtectedBranch == nil || !pr.ProtectedBranch.EnableStatusCheck {
  77. return true, nil
  78. }
  79. state, err := GetPullRequestCommitStatusState(pr)
  80. if err != nil {
  81. return false, err
  82. }
  83. return state.IsSuccess(), nil
  84. }
  85. // GetPullRequestCommitStatusState returns pull request merged commit status state
  86. func GetPullRequestCommitStatusState(pr *models.PullRequest) (structs.CommitStatusState, error) {
  87. // Ensure HeadRepo is loaded
  88. if err := pr.LoadHeadRepo(); err != nil {
  89. return "", errors.Wrap(err, "LoadHeadRepo")
  90. }
  91. // check if all required status checks are successful
  92. headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
  93. if err != nil {
  94. return "", errors.Wrap(err, "OpenRepository")
  95. }
  96. defer headGitRepo.Close()
  97. if pr.Flow == models.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) {
  98. return "", errors.New("Head branch does not exist, can not merge")
  99. }
  100. if pr.Flow == models.PullRequestFlowAGit && !git.IsReferenceExist(headGitRepo.Path, pr.GetGitRefName()) {
  101. return "", errors.New("Head branch does not exist, can not merge")
  102. }
  103. var sha string
  104. if pr.Flow == models.PullRequestFlowGithub {
  105. sha, err = headGitRepo.GetBranchCommitID(pr.HeadBranch)
  106. } else {
  107. sha, err = headGitRepo.GetRefCommitID(pr.GetGitRefName())
  108. }
  109. if err != nil {
  110. return "", err
  111. }
  112. if err := pr.LoadBaseRepo(); err != nil {
  113. return "", errors.Wrap(err, "LoadBaseRepo")
  114. }
  115. commitStatuses, err := models.GetLatestCommitStatus(pr.BaseRepo.ID, sha, models.ListOptions{})
  116. if err != nil {
  117. return "", errors.Wrap(err, "GetLatestCommitStatus")
  118. }
  119. return MergeRequiredContextsCommitStatus(commitStatuses, pr.ProtectedBranch.StatusCheckContexts), nil
  120. }