Files
autogits/workflow-pr/pr_processor.go
Adrian Schröter cc37fa73bd Add an "lfs fsck" check after submodule update
to avoid merging pull requests where lfs objects are not correctly
registered. Can happen when user has not installed lfs for example.
2026-02-26 11:12:29 +01:00

737 lines
24 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"path"
"runtime/debug"
"slices"
"strings"
"time"
"github.com/opentracing/opentracing-go/log"
"src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
"src.opensuse.org/autogits/common/gitea-generated/models"
)
func prGitBranchNameForPR(repo string, prNo int64) string {
return fmt.Sprintf("PR_%s#%d", repo, prNo)
}
func PrjGitDescription(prset *common.PRSet) (title string, desc string) {
title_refs := make([]string, 0, len(prset.PRs)-1)
refs := make([]string, 0, len(prset.PRs)-1)
prefix := ""
for _, pr := range prset.PRs {
if prset.IsPrjGitPR(pr.PR) {
continue
}
if pr.PR.State != "open" {
// remove PRs that are not open from description
continue
}
if strings.HasPrefix(pr.PR.Title, "WIP:") {
prefix = "WIP: "
}
org, repo, idx := pr.PRComponents()
title_refs = append(title_refs, repo)
ref := fmt.Sprintf(common.PrPattern, org, repo, idx)
refs = append(refs, ref)
}
slices.Sort(title_refs)
slices.Sort(refs)
title = prefix + "Forwarded PRs: " + strings.Join(title_refs, ", ")
desc = fmt.Sprintf("This is a forwarded pull request by %s\nreferencing the following pull request(s):\n\n", GitAuthor) + strings.Join(refs, "\n") + "\n"
if prset.Config.ManualMergeOnly {
desc = desc + "\n### ManualMergeOnly enabled. To merge, 'merge ok' is required in either the project PR or every package PR."
}
if prset.Config.ManualMergeProject {
desc = desc + "\n### ManualMergeProject enabled. To merge, 'merge ok' is required by project maintainer in the project PR."
}
if !prset.Config.ManualMergeOnly && !prset.Config.ManualMergeProject {
desc = desc + "\n### Automatic merge enabled. This will merge when all review requirements are satisfied."
}
return
}
func verifyRepositoryConfiguration(repo *models.Repository) error {
if repo.AutodetectManualMerge && repo.AllowManualMerge {
return nil
}
// modify repo to allow above
common.LogDebug("Adjusting repo to accept manual merges:", repo.Owner.UserName+"/"+repo.Name)
_, err := Gitea.SetRepoOptions(repo.Owner.UserName, repo.Name, true)
if err != nil {
common.LogError("Failed to set repo to manual merges:", err)
}
return err
}
func updateSubmoduleInPR(submodule, headSha string, git common.Git) {
common.LogDebug("updating submodule", submodule, "to HEAD", headSha)
// NOTE: this can fail if current PrjGit is pointing to outdated, GC'ed commit
// as long as we can update to newer one later, we are still OK
git.GitExec(common.DefaultGitPrj, "submodule", "update", "--init", "--checkout", "--depth", "1", submodule)
common.PanicOnError(git.GitExec(path.Join(common.DefaultGitPrj, submodule), "fetch", "--depth", "1", "origin", headSha))
common.PanicOnError(git.GitExec(path.Join(common.DefaultGitPrj, submodule), "checkout", "-f", headSha))
}
type PRProcessor struct {
config *common.AutogitConfig
git common.Git
}
func AllocatePRProcessor(req *models.PullRequest, configs common.AutogitConfigs) (*PRProcessor, error) {
org := req.Base.Repo.Owner.UserName
repo := req.Base.Repo.Name
id := req.Index
branch := req.Base.Ref
PRstr := fmt.Sprintf("%s/%s!%d", org, repo, id)
common.LogInfo("*** Starting processing PR:", PRstr, "branch:", branch)
config := configs.GetPrjGitConfig(org, repo, branch)
if config == nil {
if req.Base.Repo.DefaultBranch == branch {
common.LogDebug("Default branch submission...", org, repo)
config = configs.GetPrjGitConfig(org, repo, "")
}
}
if config == nil {
common.LogError("Cannot find config for PR.")
return nil, fmt.Errorf("Cannot find config for PR")
}
if common.GetLoggingLevel() >= common.LogLevelDebug {
cjson, _ := json.Marshal(config)
common.LogDebug("found config:", string(cjson))
}
if config == nil {
common.LogError("Cannot find config for branch '%s'", req.Base.Ref)
return nil, fmt.Errorf("Cannot find config for branch '%s'", req.Base.Ref)
}
git, err := GitHandler.CreateGitHandler(config.Organization)
if err != nil {
common.LogError("Cannot allocate GitHandler:", err)
return nil, fmt.Errorf("Error allocating GitHandler. Err: %w", err)
}
common.LogDebug("git path:", git.GetPath())
// git.GitExecOrPanic("", "config", "set", "--global", "advice.submoduleMergeConflict", "false")
// git.GitExecOrPanic("", "config", "set", "--global", "advice.mergeConflict", "false")
return &PRProcessor{
config: config,
git: git,
}, nil
}
func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
git := pr.git
subList, err := git.GitSubmoduleList(common.DefaultGitPrj, "HEAD")
if err != nil {
common.LogError("Error fetching submodule list for PrjGit", err)
return err
}
for _, pr := range prset.PRs {
if prset.IsPrjGitPR(pr.PR) {
continue
}
org, repo, idx := pr.PRComponents()
prHead := pr.PR.Head.Sha
revert := false
if pr.PR.State != "open" {
prjGitPR, err := prset.GetPrjGitPR()
if prjGitPR != nil {
// remove PR from PrjGit
var valid bool
if prHead, valid = git.GitSubmoduleCommitId(common.DefaultGitPrj, repo, prjGitPR.PR.MergeBase); !valid {
common.LogError("Failed fetching original submodule commit id for repo")
return err
}
}
revert = true
}
// find 'repo' in the submodule list
submodule_found := false
for submodulePath, id := range subList {
if path.Base(submodulePath) == repo {
submodule_found = true
if id != prHead {
ref := fmt.Sprintf(common.PrPattern, org, repo, idx)
commitMsg := fmt.Sprintln("auto-created for", repo, "\n\nThis commit was autocreated by", GitAuthor, "\n\nreferencing PRs:\n", ref)
if revert {
commitMsg = fmt.Sprintln("auto-created for", repo, "\n\nThis commit was autocreated by", GitAuthor, "\n\nremoving PRs:\n", ref)
}
updateSubmoduleInPR(submodulePath, prHead, git)
err := git.GitExec(path.Join(common.DefaultGitPrj, submodulePath), "lfs", "fetch")
common.LogError("lfs fetch err: ", err)
if err = git.GitExec(path.Join(common.DefaultGitPrj, submodulePath), "lfs", "fsck"); err != nil {
found_comment := false
timeline, terr := common.FetchTimelineSinceLastPush(Gitea, prHead, org, repo, idx)
if terr != nil {
common.LogError("lfs fsck error, but timeline fetch failed")
break
}
msgPrefix := "The LFS objects are broken!"
for _, t := range timeline {
if t.Type == common.TimelineCommentType_Comment && strings.HasPrefix(t.Body, msgPrefix) {
found_comment = true
common.LogError("lfs fsck Comment already found")
break
}
}
if !found_comment && !common.IsDryRun {
Gitea.AddComment(pr.PR, msgPrefix + " Please verify with 'git lfs fsck'")
}
common.LogError("lfs fsck failed with: ", err.Error())
return err
}
status, err := git.GitStatus(common.DefaultGitPrj)
common.LogDebug("status:", status)
common.LogDebug("submodule", repo, " hash:", id, " -> ", prHead)
common.PanicOnError(err)
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
pr.PR.Head.Sha = id // update the prset
}
submodule_found = true
break
}
}
if !submodule_found {
common.LogInfo("Adding new submodule", repo, "to PrjGit")
ref := fmt.Sprintf(common.PrPattern, org, repo, idx)
commitMsg := fmt.Sprintln("Add package", repo, "\n\nThis commit was autocreated by", GitAuthor, "\n\nreferencing PRs:\n", ref)
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "add", "-b", pr.PR.Base.Name, pr.PR.Base.Repo.SSHURL, repo)
updateSubmoduleInPR(repo, prHead, git)
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
}
}
return nil
}
func (pr *PRProcessor) CreatePRjGitPR(prjGitPRbranch string, prset *common.PRSet) error {
git := pr.git
PrjGitOrg, PrjGitRepo, PrjGitBranch := prset.Config.GetPrjGit()
PrjGit, err := Gitea.GetRepository(PrjGitOrg, PrjGitRepo)
if err != nil {
common.LogError("Failed to fetch PrjGit repository data.", PrjGitOrg, PrjGitRepo, err)
return err
}
RemoteName, err := git.GitClone(common.DefaultGitPrj, PrjGitBranch, PrjGit.SSHURL)
common.PanicOnError(err)
git.GitExecOrPanic(common.DefaultGitPrj, "checkout", "-B", prjGitPRbranch, RemoteName+"/"+PrjGitBranch)
headCommit, err := git.GitBranchHead(common.DefaultGitPrj, prjGitPRbranch)
if err != nil {
common.LogError("Failed to fetch PrjGit branch", prjGitPRbranch, err)
return err
}
if err := pr.SetSubmodulesToMatchPRSet(prset); err != nil {
return err
}
newHeadCommit, err := git.GitBranchHead(common.DefaultGitPrj, prjGitPRbranch)
if err != nil {
common.LogError("Failed to fetch updated PrjGit branch", prjGitPRbranch, err)
return err
}
if !common.IsDryRun && !pr.config.NoProjectGitPR {
if headCommit != newHeadCommit {
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", RemoteName, "+HEAD:"+prjGitPRbranch))
}
title, desc := PrjGitDescription(prset)
pr, err, isNew := Gitea.CreatePullRequestIfNotExist(PrjGit, prjGitPRbranch, PrjGitBranch, title, desc)
if err != nil {
common.LogError("Error creating PrjGit PR:", err)
return err
}
org := PrjGit.Owner.UserName
repo := PrjGit.Name
idx := pr.Index
if isNew {
Gitea.SetLabels(org, repo, idx, []string{prset.Config.Label(common.Label_StagingAuto)})
}
Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{
RemoveDeadline: true,
})
prinfo := prset.AddPR(pr)
prinfo.RemoteName = RemoteName
}
return nil
}
func (pr *PRProcessor) RebaseAndSkipSubmoduleCommits(prset *common.PRSet, branch string) error {
git := pr.git
PrjGitPR, err := prset.GetPrjGitPR()
common.PanicOnError(err)
remoteBranch := PrjGitPR.RemoteName + "/" + branch
common.LogDebug("Rebasing on top of", remoteBranch)
for conflict := git.GitExec(common.DefaultGitPrj, "rebase", remoteBranch); conflict != nil; {
statuses, err := git.GitStatus(common.DefaultGitPrj)
if err != nil {
git.GitExecOrPanic(common.DefaultGitPrj, "rebase", "--abort")
common.PanicOnError(err)
}
for _, s := range statuses {
if s.SubmoduleChanges != "S..." {
git.GitExecOrPanic(common.DefaultGitPrj, "rebase", "--abort")
return fmt.Errorf("Unexpected conflict in rebase. %v", s)
}
}
conflict = git.GitExec(common.DefaultGitPrj, "rebase", "--skip")
}
return nil
}
var updatePrjGitError_requeue error = errors.New("Commits do not match. Requeing after 5 seconds.")
func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
_, _, PrjGitBranch := prset.Config.GetPrjGit()
PrjGitPR, err := prset.GetPrjGitPR()
if err != nil {
common.LogError("Updating PrjGitPR but not found?", err)
return err
}
git := pr.git
if len(prset.PRs) == 1 {
if len(PrjGitPR.RemoteName) == 0 {
PrjGitPR.RemoteName, _ = git.GitClone(common.DefaultGitPrj, "", PrjGitPR.PR.Base.Repo.SSHURL)
}
git.GitExecOrPanic(common.DefaultGitPrj, "fetch", PrjGitPR.RemoteName, PrjGitPR.PR.Head.Sha)
common.LogDebug("Only project git in PR. Nothing to update.")
return nil
}
PrjGit := PrjGitPR.PR.Base.Repo
prjGitPRbranch := PrjGitPR.PR.Head.Name
if PrjGitPR.PR.Base.RepoID != PrjGitPR.PR.Head.RepoID {
// permission check, if submission comes from foreign repo
if !PrjGitPR.PR.AllowMaintainerEdit {
common.LogError("Warning: source and target branch are in different repositories. We may not have the right permissions...")
// Gitea.AddComment(PrjGitPR.PR, "This PR does not allow maintainer changes, but referenced package branch has changed!")
return nil
}
}
// PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, prjGitPRbranch, PrjGit.SSHURL)
PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, PrjGitPR.PR.Head.Ref, PrjGitPR.PR.Head.Repo.SSHURL)
common.PanicOnError(err)
git.GitExecOrPanic(common.DefaultGitPrj, "fetch", PrjGitPR.RemoteName, PrjGitBranch)
forcePush := false
// trust Gitea here on mergeability
if !PrjGitPR.PR.Mergeable {
common.PanicOnError(pr.RebaseAndSkipSubmoduleCommits(prset, PrjGitBranch))
forcePush = true
}
headCommit, err := git.GitBranchHead(common.DefaultGitPrj, prjGitPRbranch)
if err != nil {
common.LogError("Failed to fetch PrjGit branch", prjGitPRbranch, err)
return err
}
if err := pr.SetSubmodulesToMatchPRSet(prset); err != nil {
return err
}
newHeadCommit, err := git.GitBranchHead(common.DefaultGitPrj, prjGitPRbranch)
if err != nil {
common.LogError("Failed to fetch updated PrjGit branch", prjGitPRbranch, err)
return err
}
PrjGitTitle, PrjGitBody := PrjGitDescription(prset)
if PrjGitPR.PR.User.UserName == CurrentUser.UserName {
if PrjGitPR.PR.Title != PrjGitTitle || PrjGitPR.PR.Body != PrjGitBody {
common.LogDebug("New title:", PrjGitTitle)
common.LogDebug(PrjGitBody)
}
} else {
// TODO: find our first comment in timeline
}
if !common.IsDryRun {
if headCommit != PrjGitPR.PR.Head.Sha {
common.LogError("HeadCommit:", headCommit, "is not what's expected from the PR:", PrjGitPR.PR.Head.Ref, " Requeing.")
return updatePrjGitError_requeue
}
if headCommit != newHeadCommit {
params := []string{"push", PrjGitPR.RemoteName, "+HEAD:" + prjGitPRbranch}
if forcePush {
params = slices.Insert(params, 1, "-f")
}
common.PanicOnError(git.GitExec(common.DefaultGitPrj, params...))
PrjGitPR.PR.Head.Sha = newHeadCommit
Gitea.SetLabels(PrjGit.Owner.UserName, PrjGit.Name, PrjGitPR.PR.Index, []string{prset.Config.Label("PR/updated")})
}
// update PR
isPrTitleSame := func(CurrentTitle, NewTitle string) bool {
ctlen := len(CurrentTitle)
for _, suffix := range []string{"...", "…"} {
slen := len(suffix)
if ctlen > 250 && strings.HasSuffix(CurrentTitle, suffix) && len(NewTitle) > ctlen {
NewTitle = NewTitle[0:ctlen-slen] + suffix
if CurrentTitle == NewTitle {
return true
}
}
}
return CurrentTitle == NewTitle
}
if !pr.config.NoProjectGitPR && PrjGitPR.PR.User.UserName == CurrentUser.UserName && (PrjGitPR.PR.Body != PrjGitBody || !isPrTitleSame(PrjGitPR.PR.Title, PrjGitTitle)) {
Gitea.UpdatePullRequest(PrjGit.Owner.UserName, PrjGit.Name, PrjGitPR.PR.Index, &models.EditPullRequestOption{
RemoveDeadline: true,
Title: PrjGitTitle,
Body: PrjGitBody,
})
}
}
// remove closed PRs from prset
prset.RemoveClosedPRs()
return nil
}
func (pr *PRProcessor) Process(req *models.PullRequest) error {
config := pr.config
git := pr.git
// requests against project are not handled here
common.LogInfo("processing opened PR:", req.URL)
prOrg := req.Base.Repo.Owner.UserName
prRepo := req.Base.Repo.Name
prNo := req.Index
common.LogError(req)
prset, err := common.FetchPRSet(CurrentUser.UserName, Gitea, prOrg, prRepo, prNo, config)
if err != nil {
common.LogError("Cannot fetch PRSet:", err)
return err
}
common.LogInfo("fetched PRSet of size:", len(prset.PRs))
prjGitPRbranch := prGitBranchNameForPR(prRepo, prNo)
prjGitPR, err := prset.GetPrjGitPR()
if err == common.PRSet_PrjGitMissing {
if req.State != "open" {
common.LogDebug("This PR is closed and no ProjectGit PR. Ignoring.")
return nil
}
common.LogDebug("Missing PrjGit. Need to create one under branch", prjGitPRbranch)
if err = pr.CreatePRjGitPR(prjGitPRbranch, prset); err != nil {
return err
}
} else if err == nil {
common.LogDebug("Found PrjGit PR:", common.PRtoString(prjGitPR.PR))
prjGitPRbranch = prjGitPR.PR.Head.Name
if prjGitPR.PR.State != "open" {
if prjGitPR.PR.HasMerged {
// update branches in project
prjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, prjGitPRbranch, prjGitPR.PR.Base.Repo.SSHURL)
common.PanicOnError(err)
old_pkgs, err := git.GitSubmoduleList(common.DefaultGitPrj, prjGitPR.PR.MergeBase)
common.PanicOnError(err)
new_pkgs, err := git.GitSubmoduleList(common.DefaultGitPrj, prjGitPRbranch)
common.PanicOnError(err)
pkgs := make(map[string]string)
for pkg, old_commit := range old_pkgs {
if new_commit, found := new_pkgs[pkg]; found {
// pkg modified
if new_commit != old_commit {
pkgs[pkg] = new_commit
}
} else { // not found, pkg removed
pkgs[pkg] = ""
}
}
for pkg, commit := range new_pkgs {
if _, found := old_pkgs[pkg]; !found {
// pkg added
pkgs[pkg] = commit
}
}
PrjGitSubmoduleCheck(config, git, common.DefaultGitPrj, pkgs)
}
// manually merge or close entire prset that is still open
for _, pr := range prset.PRs {
if pr.PR.State == "open" {
org, repo, idx := pr.PRComponents()
if prjGitPR.PR.HasMerged {
Gitea.AddComment(pr.PR, "This PR is merged via the associated Project PR.")
err = Gitea.ManualMergePR(org, repo, idx, pr.PR.Head.Sha, false)
if _, ok := err.(*repository.RepoMergePullRequestConflict); !ok {
common.PanicOnError(err)
}
// } else {
// Gitea.AddComment(pr.PR, "Closing here because the associated Project PR has been closed.")
// Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{
// State: "closed",
// })
}
}
}
return nil
}
if len(prset.PRs) > 1 {
for _, pr := range prset.PRs {
if prset.IsPrjGitPR(pr.PR) {
continue
}
}
}
if err = pr.UpdatePrjGitPR(prset); err != nil {
return err
}
}
if prjGitPR == nil {
prjGitPR, err = prset.GetPrjGitPR()
if err == common.PRSet_PrjGitMissing && config.NoProjectGitPR {
// we could be waiting for other tooling to create the
// project git PR. In meantime, we can assign some
// reviewers here.
} else if err != nil {
common.LogError("Error fetching PrjGitPR:", err)
return nil
}
}
common.LogDebug("Updated PR")
// make sure that prjgit is consistent and only submodules that are to be *updated*
// reset anything that changed that is not part of the prset
// package removals/additions are *not* counted here
// TODO: this is broken...
if pr, err := prset.GetPrjGitPR(); err == nil && false {
common.LogDebug("Submodule parse begin")
orig_subs, err := git.GitSubmoduleList(common.DefaultGitPrj, pr.PR.MergeBase)
common.PanicOnError(err)
new_subs, err := git.GitSubmoduleList(common.DefaultGitPrj, "HEAD")
common.PanicOnError(err)
common.LogDebug("Submodule parse done")
reset_submodule := func(submodule, sha string) {
updateSubmoduleInPR(submodule, sha, git)
}
common.LogDebug("Checking we only change linked commits")
for path, commit := range new_subs {
if old, ok := orig_subs[path]; ok && old != commit {
found := false
for _, pr := range prset.PRs {
if pr.PR.Base.Repo.Name == path && commit == pr.PR.Head.Sha {
found = true
break
} else if pr.PR.Base.Repo.Name == path {
common.LogError(path, "-- commits not match", commit, pr.PR.Head.Sha)
}
}
if !found {
reset_submodule(path, old)
}
}
}
stats, err := git.GitStatus(common.DefaultGitPrj)
common.LogDebug("Check Done", len(stats), "changes")
common.PanicOnError(err)
if len(stats) > 0 {
git.GitExecOrPanic(common.DefaultGitPrj, "commit", "-a", "-m", "Sync submodule updates with PR-set")
git.GitExecQuietOrPanic(common.DefaultGitPrj, "submodule", "deinit", "--all", "--force")
if !common.IsDryRun {
git.GitExecOrPanic(common.DefaultGitPrj, "push")
}
}
}
if prjGitPR != nil {
common.LogDebug(" num of reviewers:", len(prjGitPR.PR.RequestedReviewers))
} else {
common.LogInfo("* No prjgit")
}
maintainers, err := common.FetchProjectMaintainershipData(Gitea, config)
if err != nil {
return err
}
// update prset if we should build it or not
if prjGitPR != nil {
if file, err := git.GitCatFile(common.DefaultGitPrj, prjGitPR.PR.Head.Sha, "staging.config"); err == nil {
prset.HasAutoStaging = (file != nil)
common.LogDebug(" -> automatic staging enabled?:", prset.HasAutoStaging)
}
}
// handle case where PrjGit PR is only one left and there are no changes, then we can just close the PR
if len(prset.PRs) == 1 && prjGitPR != nil && prset.PRs[0] == prjGitPR && prjGitPR.PR.User.UserName == prset.BotUser {
common.LogDebug(" --> checking if superflous PR")
diff, err := git.GitDiff(common.DefaultGitPrj, prjGitPR.PR.MergeBase, prjGitPR.PR.Head.Sha)
if err != nil {
return err
}
if len(diff) == 0 {
common.LogInfo("PR is no-op and can be closed. Closing.")
if !common.IsDryRun {
Gitea.AddComment(prjGitPR.PR, "Pull request no longer contains any changes. Closing.")
_, err = Gitea.UpdatePullRequest(prjGitPR.PR.Base.Repo.Owner.UserName, prjGitPR.PR.Base.Repo.Name, prjGitPR.PR.Index, &models.EditPullRequestOption{
State: "closed",
})
if err != nil {
return err
}
}
return nil
}
common.LogDebug(" --> NOT superflous PR")
}
for _, pr := range prset.PRs {
if err := verifyRepositoryConfiguration(pr.PR.Base.Repo); err != nil {
common.LogError("Cannot set manual merge... aborting processing")
return err
}
}
common.LogInfo("Consistent PRSet:", prset.IsConsistent())
common.LogInfo("Reviewed?", prset.IsApproved(Gitea, maintainers))
if prset.IsConsistent() && prset.IsApproved(Gitea, maintainers) {
common.LogInfo("Merging...")
if err = prset.Merge(Gitea, git); err != nil {
common.LogError("merge error:", err)
}
} else {
err = prset.AssignReviewers(Gitea, maintainers)
}
return err
}
type RequestProcessor struct {
configuredRepos map[string][]*common.AutogitConfig
recursive int
}
func (w *RequestProcessor) Process(pr *models.PullRequest) error {
configs, ok := w.configuredRepos[pr.Base.Repo.Owner.UserName]
if !ok {
return fmt.Errorf("no config found for org %s", pr.Base.Repo.Owner.UserName)
}
return ProcesPullRequest(pr, configs)
}
func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig) error {
if len(configs) < 1 {
// ignoring pull request against unconfigured project (could be just regular sources?)
return nil
}
PRProcessor, err := AllocatePRProcessor(pr, configs)
if err != nil {
log.Error(err)
return err
}
defer PRProcessor.git.Close()
return PRProcessor.Process(pr)
}
func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
defer func() {
if r := recover(); r != nil {
common.LogInfo("panic cought --- recovered")
common.LogError(string(debug.Stack()))
}
w.recursive--
}()
w.recursive++
if w.recursive > 3 {
common.LogError("Recursion limit reached... something is wrong with this PR?")
return nil
}
var pr *models.PullRequest
if req, ok := request.Data.(*common.PullRequestWebhookEvent); ok {
pr, err = Gitea.GetPullRequest(req.Pull_Request.Base.Repo.Owner.Username, req.Pull_Request.Base.Repo.Name, req.Pull_Request.Number)
if err != nil {
common.LogError("Cannot find PR for issue:", req.Pull_Request.Base.Repo.Owner.Username, req.Pull_Request.Base.Repo.Name, req.Pull_Request.Number)
return err
}
} else if req, ok := request.Data.(*common.IssueCommentWebhookEvent); ok {
pr, err = Gitea.GetPullRequest(req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
if err != nil {
common.LogError("Cannot find PR for issue:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
return err
}
} else if req, ok := request.Data.(*common.IssueWebhookEvent); ok {
issue, err := Gitea.GetIssue(req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
if err != nil {
common.LogError("Cannot find issue for issue event:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
return err
}
configs, ok := w.configuredRepos[req.Repository.Owner.Username]
if !ok {
common.LogError("*** Cannot find config for org:", req.Repository.Owner.Username)
return nil
}
processor := &IssueProcessor{
issue: issue,
}
return processor.ProcessIssue(configs)
} else {
common.LogError("*** Invalid data format for PR processing.")
return fmt.Errorf("*** Invalid data format for PR processing.")
}
configs, ok := w.configuredRepos[pr.Base.Repo.Owner.UserName]
if !ok {
common.LogError("*** Cannot find config for org:", pr.Base.Repo.Owner.UserName)
}
if err = ProcesPullRequest(pr, configs); err == updatePrjGitError_requeue {
time.Sleep(time.Second * 5)
return w.ProcessFunc(request)
}
return err
}