wip
This commit is contained in:
@@ -4,36 +4,51 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
"src.opensuse.org/autogits/common"
|
||||
)
|
||||
|
||||
type PullRequestProcessor interface {
|
||||
Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error
|
||||
func prGitBranchNameForPR(req *common.PullRequestWebhookEvent) string {
|
||||
return fmt.Sprintf("PR_%s#%d", req.Pull_Request.Base.Repo.Name, req.Pull_Request.Number)
|
||||
}
|
||||
|
||||
type RequestProcessor struct {
|
||||
Opened, Synced, Closed, Review PullRequestProcessor
|
||||
|
||||
configuredRepos map[string][]*common.AutogitConfig
|
||||
func updateSubmoduleInPR(req *common.PullRequestWebhookEvent, git common.Git) {
|
||||
common.LogDebug(req, git)
|
||||
submoduleName := req.Pull_Request.Base.Repo.Name
|
||||
commitID := req.Pull_Request.Head.Sha
|
||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "submodule", "update", "--init", "--checkout", "--depth", "1", submoduleName))
|
||||
common.PanicOnError(git.GitExec(path.Join(common.DefaultGitPrj, submoduleName), "fetch", "--depth", "1", "origin", commitID))
|
||||
common.PanicOnError(git.GitExec(path.Join(common.DefaultGitPrj, submoduleName), "checkout", commitID))
|
||||
}
|
||||
|
||||
func (w *RequestProcessor) ProcessFunc(request *common.Request) error {
|
||||
req, ok := request.Data.(*common.PullRequestWebhookEvent)
|
||||
if !ok {
|
||||
return fmt.Errorf("*** Invalid data format for PR processing.")
|
||||
}
|
||||
|
||||
configs := w.configuredRepos[req.Repository.Owner.Username]
|
||||
if len(configs) < 1 {
|
||||
// ignoring pull request against unconfigured project (could be just regular sources?)
|
||||
return nil
|
||||
}
|
||||
type PRProcessor struct {
|
||||
config *common.AutogitConfig
|
||||
git common.Git
|
||||
}
|
||||
|
||||
func AllocatePRProcessor(req *common.PullRequestWebhookEvent, configs common.AutogitConfigs) (*PRProcessor, error) {
|
||||
org := req.Pull_Request.Base.Repo.Owner.Username
|
||||
pkg := req.Pull_Request.Base.Repo.Name
|
||||
repo := req.Pull_Request.Base.Repo.Name
|
||||
id := req.Pull_Request.Number
|
||||
|
||||
branch := req.Pull_Request.Base.Ref
|
||||
assumed_git_project_name := org + "/" + pkg + "#" + branch
|
||||
assumed_git_project_name := org + "/" + repo + "#" + branch
|
||||
|
||||
PRstr := fmt.Sprintf("%s/%s#%d", org, repo, id)
|
||||
common.LogInfo("*** Starting processing PR:", PRstr)
|
||||
|
||||
c := configs.GetPrjGitConfig(org, repo, branch)
|
||||
if c == nil {
|
||||
if req.Pull_Request.Base.Repo.Default_Branch == branch {
|
||||
c = configs.GetPrjGitConfig(org, repo, "")
|
||||
}
|
||||
}
|
||||
if c == nil {
|
||||
common.LogError("Cannot find config for PR.")
|
||||
return nil, fmt.Errorf("Cannot find config for PR")
|
||||
}
|
||||
|
||||
var config *common.AutogitConfig
|
||||
for _, c := range configs {
|
||||
@@ -51,30 +66,231 @@ func (w *RequestProcessor) ProcessFunc(request *common.Request) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
common.LogDebug("found config", config)
|
||||
if config == nil {
|
||||
return fmt.Errorf("Cannot find config for branch '%s'", req.Pull_Request.Base.Ref)
|
||||
common.LogError("Cannot find config for branch '%s'", req.Pull_Request.Base.Ref)
|
||||
return nil, fmt.Errorf("Cannot find config for branch '%s'", req.Pull_Request.Base.Ref)
|
||||
}
|
||||
|
||||
git, err := GitHandler.CreateGitHandler(config.Organization)
|
||||
git, err := GitHandler.CreateGitHandler(branch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error allocating GitHandler. Err: %w", err)
|
||||
common.LogError("Cannot allocate GitHandler:", err)
|
||||
return nil, fmt.Errorf("Error allocating GitHandler. Err: %w", err)
|
||||
}
|
||||
common.LogDebug("git path:", git.GetPath())
|
||||
defer git.Close()
|
||||
|
||||
switch req.Action {
|
||||
case "opened", "reopened":
|
||||
return w.Opened.Process(req, git, config)
|
||||
case "synchronized":
|
||||
return w.Synced.Process(req, git, config)
|
||||
case "edited":
|
||||
// not need to be handled??
|
||||
return &PRProcessor{
|
||||
config: config,
|
||||
git: git,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pr *PRProcessor) CreateOrUpdatePrjGitPR(req *common.PullRequestWebhookEvent) error {
|
||||
config := pr.config
|
||||
git := pr.git
|
||||
|
||||
branchName := prGitBranchNameForPR(req)
|
||||
|
||||
org, prj, _ := config.GetPrjGit()
|
||||
prOrg := req.Pull_Request.Base.Repo.Owner.Username
|
||||
prRepo := req.Pull_Request.Base.Repo.Name
|
||||
|
||||
if org == prOrg && prj == prRepo {
|
||||
common.LogDebug("PrjGit PR. No need to update it...")
|
||||
return nil
|
||||
case "closed":
|
||||
return w.Closed.Process(req, git, config)
|
||||
case "reviewed":
|
||||
return w.Review.Process(req, git, config)
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unhandled pull request action: %s", req.Action)
|
||||
prjGit, err := Gitea.CreateRepositoryIfNotExist(git, org, prj)
|
||||
common.PanicOnErrorWithMsg(err, "Error creating a prjgitrepo:", err)
|
||||
|
||||
remoteName, err := git.GitClone(common.DefaultGitPrj, config.Branch, prjGit.SSHURL)
|
||||
common.PanicOnError(err)
|
||||
|
||||
// check if branch already there, and check that out if available
|
||||
if err := git.GitExec(common.DefaultGitPrj, "fetch", remoteName, branchName); err == nil {
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "checkout", "-B", branchName, remoteName+"/"+branchName)
|
||||
}
|
||||
|
||||
commitMsg := fmt.Sprintf(`auto-created for %s
|
||||
|
||||
This commit was autocreated by %s
|
||||
referencing
|
||||
|
||||
`+common.PrPattern,
|
||||
prRepo,
|
||||
GitAuthor,
|
||||
prOrg,
|
||||
prRepo,
|
||||
req.Pull_Request.Number,
|
||||
)
|
||||
|
||||
subList, err := git.GitSubmoduleList(common.DefaultGitPrj, "HEAD")
|
||||
common.PanicOnError(err)
|
||||
|
||||
if id := subList[prRepo]; id != req.Pull_Request.Head.Sha {
|
||||
updateSubmoduleInPR(req, git)
|
||||
status, err := git.GitStatus(common.DefaultGitPrj)
|
||||
common.LogDebug("status:", status)
|
||||
common.LogDebug("submodule", prRepo, " hash:", id, " -> ", req.Pull_Request.Head.Sha)
|
||||
common.PanicOnError(err)
|
||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
|
||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", remoteName, "+HEAD:"+branchName))
|
||||
}
|
||||
|
||||
_, err = Gitea.CreatePullRequestIfNotExist(prjGit, branchName, prjGit.DefaultBranch,
|
||||
fmt.Sprintf("Forwarded PR: %s", prRepo),
|
||||
fmt.Sprintf(`This is a forwarded pull request by %s
|
||||
referencing the following pull request:
|
||||
|
||||
`+common.PrPattern,
|
||||
GitAuthor, prOrg, prRepo, req.Pull_Request.Number),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (pr *PRProcessor) Process(req *common.PullRequestWebhookEvent) error {
|
||||
config := pr.config
|
||||
git := pr.git
|
||||
|
||||
// requests against project are not handled here
|
||||
common.LogInfo("processing opened PR:", req.Pull_Request.Url)
|
||||
prOrg := req.Pull_Request.Base.Repo.Owner.Username
|
||||
prRepo := req.Pull_Request.Base.Repo.Name
|
||||
|
||||
common.LogError(req)
|
||||
|
||||
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
|
||||
if prOrg != prjGitOrg || prRepo != prjGitRepo {
|
||||
if err := pr.CreateOrUpdatePrjGitPR(req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
prset, err := common.FetchPRSet(CurrentUser.UserName, Gitea, prOrg, prRepo, req.Number, config)
|
||||
if err != nil {
|
||||
common.LogError("Cannot fetch PRSet:", err)
|
||||
return err
|
||||
}
|
||||
common.LogInfo("fetched PRSet of size:", len(prset.PRs))
|
||||
|
||||
// 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
|
||||
if pr, err := prset.GetPrjGitPR(); err == nil {
|
||||
remote, err := git.GitClone(common.DefaultGitPrj, prjGitBranch, pr.Base.Repo.CloneURL)
|
||||
common.PanicOnError(err)
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "fetch", remote, pr.Base.Ref, pr.Head.Ref)
|
||||
|
||||
common.LogDebug("Fetch done")
|
||||
orig_subs, err := git.GitSubmoduleList(common.DefaultGitPrj, pr.Base.Sha)
|
||||
common.PanicOnError(err)
|
||||
new_subs, err := git.GitSubmoduleList(common.DefaultGitPrj, pr.Head.Sha)
|
||||
common.PanicOnError(err)
|
||||
common.LogDebug("Submodule parse done")
|
||||
|
||||
reset_submodule := func(submodule, sha string) {
|
||||
spath := path.Join(common.DefaultGitPrj, submodule)
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "update", "--init", "--depth", "1", submodule)
|
||||
git.GitExecOrPanic(spath, "fetch", "origin", sha)
|
||||
git.GitExecOrPanic(spath, "checkout", sha)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
reset_submodule(path, old)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stats, err := git.GitStatus(common.DefaultGitPrj)
|
||||
common.PanicOnError(err)
|
||||
if len(stats) > 0 {
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "commit", "-a", "-m", "Sync submodule updates with PR-set")
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "deinit", "--all", "--force")
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "push")
|
||||
}
|
||||
}
|
||||
|
||||
// request build review
|
||||
PR, err := prset.GetPrjGitPR()
|
||||
if err != nil {
|
||||
common.LogError("Error fetching PrjGitPR:", err)
|
||||
return nil
|
||||
}
|
||||
common.LogDebug(" num of reviewers:", len(PR.RequestedReviewers))
|
||||
org, repo, branch := config.GetPrjGit()
|
||||
maintainers, err := common.FetchProjectMaintainershipData(Gitea, org, repo, branch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = prset.AssignReviewers(Gitea, maintainers)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type PullRequestProcessor interface {
|
||||
Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error
|
||||
}
|
||||
|
||||
type RequestProcessor struct {
|
||||
configuredRepos map[string][]*common.AutogitConfig
|
||||
}
|
||||
|
||||
func ProcesPullRequest(req *common.PullRequestWebhookEvent, configs []*common.AutogitConfig) error {
|
||||
if len(configs) < 1 {
|
||||
// ignoring pull request against unconfigured project (could be just regular sources?)
|
||||
return nil
|
||||
}
|
||||
|
||||
if req.Pull_Request.State != "open" {
|
||||
common.LogError("Can only deal with open PRs. This one is", req.Pull_Request.State)
|
||||
return nil
|
||||
}
|
||||
|
||||
pr, err := AllocatePRProcessor(req, configs)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
defer pr.git.Close()
|
||||
|
||||
switch req.Action {
|
||||
case "opened", "reopened", "synchronized", "edited", "closed", "reviewed":
|
||||
return pr.Process(req)
|
||||
}
|
||||
|
||||
common.LogError("Unhandled pull request action:", req.Action)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *RequestProcessor) ProcessFunc(request *common.Request) error {
|
||||
req, ok := request.Data.(*common.PullRequestWebhookEvent)
|
||||
if !ok {
|
||||
common.LogError("*** Invalid data format for PR processing.")
|
||||
return fmt.Errorf("*** Invalid data format for PR processing.")
|
||||
}
|
||||
|
||||
org := req.Repository.Owner.Username
|
||||
configs := w.configuredRepos[org]
|
||||
return ProcesPullRequest(req, configs)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user