package common import ( "regexp" "slices" "strings" "src.opensuse.org/autogits/common/gitea-generated/models" ) type PRReviews struct { reviews []*models.PullReview reviewers []string comments []*models.TimelineComment } func FetchGiteaReviews(rf GiteaReviewTimelineFetcher, reviewers []string, org, repo string, no int64) (*PRReviews, error) { timeline, err := rf.GetTimeline(org, repo, no) if err != nil { return nil, err } rawReviews, err := rf.GetPullRequestReviews(org, repo, no) if err != nil { return nil, err } reviews := make([]*models.PullReview, 0, 10) var comments []*models.TimelineComment for idx, item := range timeline { if item.Type == TimelineCommentType_Review { for _, r := range rawReviews { if r.ID == item.ReviewID { reviews = append(reviews, r) break } } } else if item.Type == TimelineCommentType_Comment { comments = append(comments, item) } else if item.Type == TimelineCommentType_PushPull { timeline = timeline[0:idx] break } else { LogDebug("Unhandled timeline type:", item.Type) } } return &PRReviews{ reviews: reviews, reviewers: reviewers, comments: comments, }, nil } const ManualMergeOK = "^merge\\s+ok(\\W|$)" var merge_ok_regex *regexp.Regexp = regexp.MustCompile(ManualMergeOK) func bodyCommandManualMergeOK(body string) bool { lines := SplitLines(body) for _, line := range lines { if merge_ok_regex.MatchString(strings.ToLower(line)) { return true } } return false } func (r *PRReviews) IsManualMergeOK() bool { for _, c := range r.comments { if c.Updated != c.Created { continue } if slices.Contains(r.reviewers, c.User.UserName) { if bodyCommandManualMergeOK(c.Body) { return true } } } for _, c := range r.reviews { if c.Updated != c.Submitted { continue } if slices.Contains(r.reviewers, c.User.UserName) { if bodyCommandManualMergeOK(c.Body) { return true } } } return false } func (r *PRReviews) IsApproved() bool { goodReview := true for _, reviewer := range r.reviewers { goodReview = false for _, review := range r.reviews { if review.User.UserName == reviewer && review.State == ReviewStateApproved && !review.Stale && !review.Dismissed { LogDebug(" -- found review: ", review.User.UserName) goodReview = true break } } if !goodReview { break } } return goodReview } func (r *PRReviews) HasPendingReviewBy(reviewer string) bool { if !slices.Contains(r.reviewers, reviewer) { return false } isPending := false for _, r := range r.reviews { if r.User.UserName == reviewer && !r.Stale { switch r.State { case ReviewStateApproved: fallthrough case ReviewStateRequestChanges: return false case ReviewStateRequestReview: fallthrough case ReviewStatePending: isPending = true } } } return isPending } func (r *PRReviews) IsReviewedBy(reviewer string) bool { if !slices.Contains(r.reviewers, reviewer) { return false } for _, r := range r.reviews { if r.User.UserName == reviewer && !r.Stale { switch r.State { case ReviewStateApproved: return true case ReviewStateRequestChanges: return true } } } return false }