mirror of
https://codeberg.org/forgejo/forgejo
synced 2025-10-19 00:40:51 +02:00
The codeowner features computes the mergebase (I'm not exactly sure why, because this should be stored in the database in `merge_base` column, but if there's no harm to compute it again as that will always be the correct answer) in order to get the changed files between the merge base and the head commit. To do this a function was used that adds a remote... my best reasoning is that this was done because the only function that that was exported on the repository struct had this requirement. Add a new function that *simply* computes the merge base without requiring a remote. The main benefit of not using a remote is that within Codeberg we are frequently seeing `config.lock` being lingered around (see forgejo/forgejo#1946) so its best to avoid modifying the config when possible - in this case it was completely unnecessary. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9610 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
132 lines
3.6 KiB
Go
132 lines
3.6 KiB
Go
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package issue
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
issues_model "forgejo.org/models/issues"
|
|
org_model "forgejo.org/models/organization"
|
|
access_model "forgejo.org/models/perm/access"
|
|
"forgejo.org/models/unit"
|
|
user_model "forgejo.org/models/user"
|
|
"forgejo.org/modules/git"
|
|
"forgejo.org/modules/gitrepo"
|
|
"forgejo.org/modules/log"
|
|
"forgejo.org/modules/setting"
|
|
)
|
|
|
|
type ReviewRequestNotifier struct {
|
|
Comment *issues_model.Comment
|
|
IsAdd bool
|
|
Reviewer *user_model.User
|
|
ReviewTeam *org_model.Team
|
|
}
|
|
|
|
func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) {
|
|
if pr.IsWorkInProgress(ctx) {
|
|
return nil, nil
|
|
}
|
|
|
|
if err := pr.LoadHeadRepo(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := pr.LoadBaseRepo(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if pr.BaseRepo.IsFork {
|
|
return nil, nil
|
|
}
|
|
|
|
repo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer repo.Close()
|
|
|
|
commit, err := repo.GetBranchCommit(pr.BaseRepo.DefaultBranch)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var rules []*issues_model.CodeOwnerRule
|
|
for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS", ".forgejo/CODEOWNERS"} {
|
|
if blob, err := commit.GetBlobByPath(file); err == nil {
|
|
rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize)
|
|
if err == nil {
|
|
rules, _ = issues_model.GetCodeOwnersFromReader(ctx, rc, size > setting.UI.MaxDisplayFileSize)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
mergeBase, err := repo.GetMergeBaseSimple(git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// https://github.com/go-gitea/gitea/issues/29763, we need to get the files changed
|
|
// between the merge base and the head commit but not the base branch and the head commit
|
|
changedFiles, err := repo.GetFilesChangedBetween(mergeBase, pr.GetGitRefName())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
uniqUsers := make(map[int64]*user_model.User)
|
|
uniqTeams := make(map[string]*org_model.Team)
|
|
for _, rule := range rules {
|
|
for _, f := range changedFiles {
|
|
if (rule.Rule.MatchString(f) && !rule.Negative) || (!rule.Rule.MatchString(f) && rule.Negative) {
|
|
for _, u := range rule.Users {
|
|
uniqUsers[u.ID] = u
|
|
}
|
|
for _, t := range rule.Teams {
|
|
uniqTeams[fmt.Sprintf("%d/%d", t.OrgID, t.ID)] = t
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
notifiers := make([]*ReviewRequestNotifier, 0, len(uniqUsers)+len(uniqTeams))
|
|
|
|
if err := issue.LoadPoster(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, u := range uniqUsers {
|
|
permission, err := access_model.GetUserRepoPermission(ctx, issue.Repo, u)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GetUserRepoPermission: %w", err)
|
|
}
|
|
if u.ID != issue.Poster.ID && permission.CanRead(unit.TypePullRequests) {
|
|
comment, err := issues_model.AddReviewRequest(ctx, issue, u, issue.Poster)
|
|
if err != nil {
|
|
log.Warn("Failed add assignee user: %s to PR review: %s#%d, error: %s", u.Name, pr.BaseRepo.Name, pr.ID, err)
|
|
return nil, err
|
|
}
|
|
notifiers = append(notifiers, &ReviewRequestNotifier{
|
|
Comment: comment,
|
|
IsAdd: true,
|
|
Reviewer: u,
|
|
})
|
|
}
|
|
}
|
|
for _, t := range uniqTeams {
|
|
comment, err := issues_model.AddTeamReviewRequest(ctx, issue, t, issue.Poster)
|
|
if err != nil {
|
|
log.Warn("Failed add assignee team: %s to PR review: %s#%d, error: %s", t.Name, pr.BaseRepo.Name, pr.ID, err)
|
|
return nil, err
|
|
}
|
|
notifiers = append(notifiers, &ReviewRequestNotifier{
|
|
Comment: comment,
|
|
IsAdd: true,
|
|
ReviewTeam: t,
|
|
})
|
|
}
|
|
|
|
return notifiers, nil
|
|
}
|