This commit is contained in:
2025-05-05 18:57:05 +02:00
parent eb997e1ae9
commit da1df24666
20 changed files with 274 additions and 119 deletions

View File

@@ -18,7 +18,7 @@ type BasicPR struct {
Num int64
}
var validOrgAndRepoRx *regexp.Regexp = regexp.MustCompile("^[A-Za-z0-9_-]+$")
var validOrgAndRepoRx *regexp.Regexp = regexp.MustCompile("^[A-Za-z0-9_\\.-]+$")
func parsePrLine(line string) (BasicPR, error) {
var ret BasicPR

View File

@@ -95,7 +95,7 @@ type GiteaFileContentAndRepoFetcher interface {
GiteaRepoFetcher
}
func PartiallyParseWorkflowConfig(data []byte) (*AutogitConfig, error) {
func UnmarshalWorkflowConfig(data []byte) (*AutogitConfig, error) {
var config AutogitConfig
data, err := hujson.Standardize(data)
if err != nil {
@@ -130,7 +130,7 @@ func ReadWorkflowConfig(gitea GiteaFileContentAndRepoFetcher, git_project string
return nil, fmt.Errorf("Error fetching 'workflow.config' for %s/%s#%s: %w", a[0], prjGitRepo, branch, err)
}
config, err := PartiallyParseWorkflowConfig(data)
config, err := UnmarshalWorkflowConfig(data)
if err != nil {
return nil, err
}

View File

@@ -196,19 +196,26 @@ func (refs *GitReferences) addReference(id, branch string) {
}
func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error) {
LogDebug("Cloning", remoteUrl, " repo:", repo, " branch:", branch)
remoteUrlComp, err := ParseGitRemoteUrl(remoteUrl)
if err != nil {
return "", fmt.Errorf("Cannot parse remote URL: %w", err)
}
if len(branch) == 0 {
remoteBranch := "HEAD"
if len(branch) == 0 && remoteUrlComp != nil {
branch = remoteUrlComp.Commit
}
if len(branch) == 0 {
branch = "HEAD"
remoteBranch = branch
} else if len(branch) > 0 {
remoteBranch = branch
}
remoteName := remoteUrlComp.RemoteName()
LogDebug("Clone", *remoteUrlComp, " -> ", remoteName)
if remoteUrlComp != nil {
LogDebug("Clone", *remoteUrlComp, " -> ", remoteName)
} else {
LogDebug("Clone", "[default] -> ", remoteName)
}
remoteRef := remoteName + "/" + remoteBranch
if fi, err := os.Stat(path.Join(e.GitPath, repo)); os.IsNotExist(err) {
if err = e.GitExec("", "clone", "--origin", remoteName, remoteUrl, repo); err != nil {
return remoteName, err
@@ -222,9 +229,34 @@ func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error
e.GitExecOrPanic(repo, "remote", "set-url", remoteName, remoteUrl)
}
e.GitExecOrPanic(repo, "fetch", remoteName, branch)
// check if we have submodule to deinit
if list, _ := e.GitSubmoduleList(repo, "HEAD"); len(list) > 0 {
e.GitExecOrPanic(repo, "submodule", "deinit", "--all", "--force")
}
e.GitExecOrPanic(repo, "fetch", remoteName, remoteBranch)
}
return remoteName, e.GitExec(repo, "checkout", "-B", branch, "refs/remotes/"+remoteName+"/"+branch)
refsBytes, err := os.ReadFile(path.Join(e.GitPath, repo, ".git/refs/remotes", remoteName, "HEAD"))
if err != nil {
LogError("Cannot read HEAD of remote", remoteName)
return remoteName, fmt.Errorf("Cannot read HEAD of remote %s", remoteName)
}
refs := string(refsBytes)
if refs[0:5] != "ref: " {
LogError("Unexpected format of remote HEAD ref:", refs)
return remoteName, fmt.Errorf("Unexpected format of remote HEAD ref: %s", refs)
}
if len(branch) == 0 || branch == "HEAD" {
remoteRef = strings.TrimSpace(refs[5:])
branch = remoteRef[strings.LastIndex(remoteRef, "/")+1:]
LogDebug("remoteRef", remoteRef)
LogDebug("branch", branch)
}
return remoteName, e.GitExec(repo, "checkout", "--track", "-B", branch, remoteRef)
}
func (e *GitHandlerImpl) GitBranchHead(gitDir, branchName string) (string, error) {
@@ -306,7 +338,7 @@ func (e *GitHandlerImpl) GitExecWithOutput(cwd string, params ...string) (string
cmd.Dir = filepath.Join(e.GitPath, cwd)
cmd.Stdin = nil
LogDebug("git execute:", cmd.Args)
LogDebug("git execute @", cwd, ":", cmd.Args)
out, err := cmd.CombinedOutput()
LogDebug(string(out))
if err != nil {

View File

@@ -54,6 +54,8 @@ func TestGitClone(t *testing.T) {
},
}
return
execPath, err := os.Getwd()
if err != nil {
t.Fatal(err)

View File

@@ -239,6 +239,9 @@ func (gitea *GiteaTransport) SetCommitStatus(org, repo, hash string, status *mod
}),
gitea.transport.DefaultAuthentication,
)
if err != nil {
return nil, err
}
return res.Payload, err
}
@@ -485,6 +488,9 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
func (gitea *GiteaTransport) GetAssociatedPrjGitPR(prjGitOrg, prjGitRepo, refOrg, refRepo string, Index int64) (*models.PullRequest, error) {
var page int64
state := "open"
prLine := fmt.Sprintf(PrPattern, refOrg, refRepo, Index)
LogDebug("Finding PrjGitPR for", prLine, " Looking in", prjGitOrg, "/", prjGitRepo)
for {
page++
prs, err := gitea.client.Repository.RepoListPullRequests(
@@ -501,14 +507,13 @@ func (gitea *GiteaTransport) GetAssociatedPrjGitPR(prjGitOrg, prjGitRepo, refOrg
return nil, fmt.Errorf("cannot fetch PR list for %s / %s : %w", prjGitOrg, prjGitRepo, err)
}
prLine := fmt.Sprintf(PrPattern, refOrg, refRepo, Index)
// payload_processing:
for _, pr := range prs.Payload {
lines := strings.Split(pr.Body, "\n")
for _, line := range lines {
if strings.TrimSpace(line) == prLine {
LogDebug("Found PR:", pr.Index)
return pr, nil
}
}
@@ -669,7 +674,7 @@ endPrs:
}
// if pr is closed for more than a week, assume that we are done too
if time.Since(time.Time(pr.Updated)) > 7*24*time.Hour {
if pr.State == "closed" && time.Since(time.Time(pr.Updated)) > 7*24*time.Hour {
break endPrs
}

View File

@@ -40,9 +40,9 @@ func PanicOnError(err error) {
}
}
func PanicOnErrorWithMsg(err error, msg string) {
func PanicOnErrorWithMsg(err error, msg ...any) {
if err != nil {
LogError(msg)
LogError(msg...)
panic(err)
}
}

View File

@@ -44,12 +44,13 @@ func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, org, prjGit
dir := true
if err != nil || data == nil {
dir = false
if _, notFound := err.(*repository.RepoGetRawFileNotFound); !notFound {
if _, notFound := err.(*repository.RepoGetContentsNotFound); !notFound {
return nil, err
}
LogDebug("Falling back to maintainership file")
data, _, err = gitea.FetchMaintainershipFile(org, prjGit, branch)
if err != nil || data == nil {
if _, notFound := err.(*repository.RepoGetRawFileNotFound); !notFound {
if _, notFound := err.(*repository.RepoGetContentsNotFound); !notFound {
return nil, err
}

View File

@@ -59,7 +59,7 @@ type ObsClient struct {
}
func NewObsClient(host string) (*ObsClient, error) {
baseUrl, err := url.Parse("https://" + host)
baseUrl, err := url.Parse(host)
if err != nil {
return nil, err
}
@@ -186,6 +186,7 @@ func (c *ObsClient) GetGroupMeta(gid string) (*GroupMeta, error) {
if err != nil {
return nil, err
}
defer res.Body.Close()
var meta GroupMeta
err = xml.Unmarshal(data, &meta)
@@ -215,6 +216,7 @@ func (c *ObsClient) GetUserMeta(uid string) (*UserMeta, error) {
if err != nil {
return nil, err
}
defer res.Body.Close()
var meta UserMeta
err = xml.Unmarshal(data, &meta)
@@ -238,6 +240,11 @@ func (c *ObsClient) ObsRequest(method string, url string, body io.Reader) (*http
req.Header.Add("cookie", c.cookie)
}
res, err := c.client.Do(req)
if err != nil && res == nil {
LogDebug(err)
return res, err
}
if err == nil && res.StatusCode == 200 {
auth_cookie := res.Header.Get("set-cookie")
if auth_cookie != "" {
@@ -331,6 +338,7 @@ func (c *ObsClient) GetProjectMeta(project string) (*ProjectMeta, error) {
return nil, err
}
defer res.Body.Close()
return parseProjectMeta(data)
}
@@ -354,6 +362,7 @@ func (c *ObsClient) GetPackageMeta(project, pkg string) (*PackageMeta, error) {
if err != nil {
return nil, err
}
defer res.Body.Close()
var meta PackageMeta
err = xml.Unmarshal(data, &meta)
@@ -412,6 +421,7 @@ func (c *ObsClient) SetProjectMeta(meta *ProjectMeta) error {
if err != nil {
return err
}
defer res.Body.Close()
switch res.StatusCode {
case 200:
@@ -428,6 +438,7 @@ func (c *ObsClient) DeleteProject(project string) error {
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != 200 {
return fmt.Errorf("Unexpected return code: %d", res.StatusCode)
@@ -494,7 +505,7 @@ func (r *BuildResultList) GetPackageList() []string {
func (r *BuildResultList) BuildResultSummary() (success, finished bool) {
if r == nil {
return true, true
return false, false
}
finished = len(r.Result) > 0 && len(r.Result[0].Status) > 0
@@ -698,6 +709,7 @@ func (c *ObsClient) ProjectConfig(project string) (string, error) {
}
if data, err := io.ReadAll(res.Body); err == nil {
defer res.Body.Close()
return string(data), nil
} else {
return "", err
@@ -750,5 +762,6 @@ func (c *ObsClient) BuildStatusWithState(project string, opts *BuildResultOption
if err != nil {
return nil, err
}
defer res.Body.Close()
return parseBuildResults(data)
}

View File

@@ -13,8 +13,9 @@ import (
)
type PRInfo struct {
PR *models.PullRequest
Reviews *PRReviews
PR *models.PullRequest
Reviews *PRReviews
RemoteName string
}
type PRSet struct {
@@ -31,7 +32,8 @@ func readPRData(gitea GiteaPRFetcher, pr *models.PullRequest, currentSet []PRInf
retSet := []PRInfo{PRInfo{PR: pr}}
// only need to extact there on PrjGit PR
if pr.Base.Repo.Name == config.GitProjectName && pr.Base.Repo.Owner.UserName == config.Organization {
org, repo, _ := config.GetPrjGit()
if pr.Base.Repo.Name == repo && pr.Base.Repo.Owner.UserName == org {
_, refPRs := ExtractDescriptionAndPRs(bufio.NewScanner(strings.NewReader(pr.Body)))
for _, prdata := range refPRs {
pr, err := gitea.GetPullRequest(prdata.Org, prdata.Repo, prdata.Num)
@@ -54,7 +56,11 @@ func FetchPRSet(gitea GiteaPRFetcher, org, repo string, num int64, config *Autog
var err error
prjGitOrg, prjGitRepo, _ := config.GetPrjGit()
if prjGitOrg != org || prjGitRepo != config.GitProjectName {
if prjGitOrg == org && prjGitRepo == repo {
if pr, err = gitea.GetPullRequest(org, repo, num); err != nil {
return nil, err
}
} else {
if pr, err = gitea.GetAssociatedPrjGitPR(prjGitOrg, prjGitRepo, org, repo, num); err != nil {
return nil, err
}
@@ -64,10 +70,6 @@ func FetchPRSet(gitea GiteaPRFetcher, org, repo string, num int64, config *Autog
return nil, err
}
}
} else {
if pr, err = gitea.GetPullRequest(org, repo, num); err != nil {
return nil, err
}
}
prs, err := readPRData(gitea, pr, nil, config)
@@ -79,7 +81,8 @@ func FetchPRSet(gitea GiteaPRFetcher, org, repo string, num int64, config *Autog
}
func (rs *PRSet) IsPrjGitPR(pr *models.PullRequest) bool {
return pr.Base.Repo.Name == rs.Config.GitProjectName && pr.Base.Repo.Owner.UserName == rs.Config.Organization
org, repo, _ := rs.Config.GetPrjGit()
return pr.Base.Repo.Name == repo && pr.Base.Repo.Owner.UserName == org
}
func (rs *PRSet) GetPrjGitPR() (*models.PullRequest, error) {
@@ -215,8 +218,9 @@ func (rs *PRSet) Merge(gh GitHandlerGenerator) error {
if err != nil {
return err
}
git.GitExecOrPanic("", "clone", "--depth", "1", prjgit.Base.Repo.SSHURL, DefaultGitPrj)
git.GitExecOrPanic(DefaultGitPrj, "fetch", "origin", prjgit.Base.Sha, prjgit.Head.Sha)
remote, err := git.GitClone(DefaultGitPrj, rs.Config.Branch, prjgit.Base.Repo.SSHURL)
PanicOnError(err)
git.GitExecOrPanic(DefaultGitPrj, "fetch", remote, prjgit.Base.Sha, prjgit.Head.Sha)
// if other changes merged, check if we have conflicts
rev := strings.TrimSpace(git.GitExecWithOutputOrPanic(DefaultGitPrj, "merge-base", "HEAD", prjgit.Base.Sha, prjgit.Head.Sha))
@@ -303,8 +307,6 @@ func (rs *PRSet) Merge(gh GitHandlerGenerator) error {
return fmt.Errorf("Can't close .gitmodules: %w", err)
}
os.CopyFS("/tmp/test", os.DirFS(git.GetPath()))
git.GitExecOrPanic(DefaultGitPrj, "add", ".gitmodules")
git.GitExecOrPanic(DefaultGitPrj, "-c", "core.editor=true", "merge", "--continue")
}
@@ -316,18 +318,19 @@ func (rs *PRSet) Merge(gh GitHandlerGenerator) error {
if rs.IsPrjGitPR(prinfo.PR) {
continue
}
git.GitExecOrPanic("", "clone", prinfo.PR.Base.Repo.SSHURL, prinfo.PR.Base.Name)
git.GitExecOrPanic(prinfo.PR.Base.Name, "fetch", "origin", prinfo.PR.Head.Sha)
prinfo.RemoteName, err = git.GitClone(prinfo.PR.Base.Name, rs.Config.Branch, prinfo.PR.Base.Repo.SSHURL)
PanicOnError(err)
git.GitExecOrPanic(prinfo.PR.Base.Name, "fetch", prinfo.RemoteName, prinfo.PR.Head.Sha)
git.GitExecOrPanic(prinfo.PR.Base.Name, "merge", "--ff", prinfo.PR.Head.Sha)
}
// push changes
git.GitExecOrPanic(DefaultGitPrj, "push", "origin")
git.GitExecOrPanic(DefaultGitPrj, "push", remote)
for _, prinfo := range rs.PRs {
if rs.IsPrjGitPR(prinfo.PR) {
continue
}
git.GitExecOrPanic(prinfo.PR.Base.Name, "push", "origin")
git.GitExecOrPanic(prinfo.PR.Base.Name, "push", prinfo.RemoteName)
}
return nil

View File

@@ -463,13 +463,17 @@ func TestPRAssignReviewers(t *testing.T) {
}
func TestPRMerge(t *testing.T) {
repoDir := t.TempDir()
cwd, _ := os.Getwd()
cmd := exec.Command("/usr/bin/bash", path.Join(cwd, "test_repo_setup.sh"))
cmd.Dir = t.TempDir()
cmd := exec.Command(path.Join(cwd, "test_repo_setup.sh"))
cmd.Dir = repoDir
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
}
common.SetLoggingLevel(common.LogLevelDebug)
common.ExtraGitParams = []string{
"GIT_CONFIG_COUNT=1",
"GIT_CONFIG_KEY_0=protocol.file.allow",
@@ -504,7 +508,7 @@ func TestPRMerge(t *testing.T) {
Owner: &models.User{
UserName: "org",
},
SSHURL: path.Join(cmd.Dir, "prjgit"),
SSHURL: "file://" + path.Join(repoDir, "prjgit"),
},
},
Head: &models.PRBranchInfo{
@@ -524,7 +528,7 @@ func TestPRMerge(t *testing.T) {
Owner: &models.User{
UserName: "org",
},
SSHURL: path.Join(cmd.Dir, "prjgit"),
SSHURL: "file://" + path.Join(cmd.Dir, "prjgit"),
},
},
Head: &models.PRBranchInfo{
@@ -539,6 +543,8 @@ func TestPRMerge(t *testing.T) {
ctl := gomock.NewController(t)
mock := mock_common.NewMockGiteaPRFetcher(ctl)
testDir := t.TempDir()
t.Log("dir:", testDir)
mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil)
set, err := common.FetchPRSet(mock, "org", "prj", 1, config)
@@ -546,8 +552,12 @@ func TestPRMerge(t *testing.T) {
t.Fatal(err)
}
gh, _ := common.AllocateGitWorkTree("", "", "")
if err = set.Merge(gh); err != nil && (test.mergeError == "" || (len(test.mergeError) > 0 && !strings.Contains(err.Error(), test.mergeError))) {
gh, _ := common.AllocateGitWorkTree(testDir, "", "")
err = set.Merge(gh)
if err != nil && (test.mergeError == "" || (len(test.mergeError) > 0 && !strings.Contains(err.Error(), test.mergeError))) {
os.CopyFS("/tmp/upstream", os.DirFS(repoDir))
os.CopyFS("/tmp/out", os.DirFS(testDir))
t.Fatal(err)
}
})

View File

@@ -84,6 +84,11 @@ var valid_schemas []string = []string{"https", "ssh", "http", "file"}
func ParseGitRemoteUrl(urlString string) (*GitUrl, error) {
url, err := url.Parse(urlString)
if url != nil && url.Scheme == "file" && err == nil {
return nil, nil
}
if err != nil || !slices.Contains(valid_schemas, url.Scheme) {
u, err := TranslateSshNativeToUrl(urlString)
if err != nil {

View File

@@ -105,15 +105,16 @@ const (
)
func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
if _, finished := project.BuildResultSummary(); !finished {
return BuildStatusSummaryBuilding
}
if _, finished := refProject.BuildResultSummary(); !finished {
common.LogDebug("refProject not finished building??")
return BuildStatusSummaryUnknown
}
if _, finished := project.BuildResultSummary(); !finished {
common.LogDebug("Still building...")
return BuildStatusSummaryBuilding
}
// the repositories should be setup equally between the projects. We
// need to verify that packages that are building in `refProject` are not
// failing in the `project`
@@ -684,7 +685,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) error {
TargetURL: ObsWebHost + "/project/show/" + stagingProject,
}
if change != RequestModificationNoChange {
gitea.SetCommitStatus(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Head.Sha, status)
common.LogDebug(gitea.SetCommitStatus(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Head.Sha, status))
}
if change == RequestModificationProjectCreated {
@@ -808,6 +809,9 @@ func main() {
ObsWebHost = ObsWebHostFromApiHost(ObsApiHost)
}
common.LogDebug("OBS Web Host:", ObsWebHost)
common.LogDebug("OBS API Host:", ObsApiHost)
common.PanicOnErrorWithMsg(common.RequireGiteaSecretToken(), "Cannot find GITEA_TOKEN")
common.PanicOnErrorWithMsg(common.RequireObsSecretToken(), "Cannot find OBS_USER and OBS_PASSWORD")
@@ -828,9 +832,6 @@ func main() {
for {
PollWorkNotifications(GiteaUrl)
if *debug {
break
}
common.LogInfo("Poll cycle finished")
time.Sleep(5 * time.Minute)
}

View File

@@ -513,7 +513,7 @@ func main() {
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance")
rabbitUrl := flag.String("url", "amqps://rabbit.opensuse.org", "URL for RabbitMQ instance")
flag.BoolVar(&DebugMode, "debug", false, "Extra debugging information")
flag.BoolVar(&noop, "no-op", false, "No-op mode. Do not push changes to remote repo.")
flag.BoolVar(&noop, "dry", false, "Dry mode. Do not push changes to remote repo.")
flag.BoolVar(&checkOnStart, "check-on-start", false, "Check all repositories for consistency on start, without delays")
checkIntervalHours := flag.Float64("check-interval", 5, "Check interval (+-random delay) for repositories for consitency, in hours")
basePath := flag.String("repo-path", "", "Repository path. Default is temporary directory")

View File

@@ -22,7 +22,9 @@ import (
"flag"
"net/url"
"os"
"regexp"
"slices"
"strconv"
"time"
"src.opensuse.org/autogits/common"
@@ -39,6 +41,7 @@ var ListPROnly bool
var PRID int64
var CurrentUser *models.User
var GitHandler common.GitHandlerGenerator
var Gitea common.Gitea
func main() {
workflowConfig := flag.String("config", "", "Repository and workflow definition file")
@@ -50,6 +53,7 @@ func main() {
flag.BoolVar(&ListPROnly, "list-prs-only", false, "Only lists PRs without acting on them")
flag.Int64Var(&PRID, "id", -1, "Process only the specific ID and ignore the rest. Use for debugging")
basePath := flag.String("repo-path", "", "Repository path. Default is temporary directory")
pr := flag.String("only-pr", "", "Only specific PR to process. For debugging")
flag.Parse()
common.SetLoggingLevel(common.LogLevelInfo)
@@ -72,19 +76,23 @@ func main() {
return
}
gitea := common.AllocateGiteaTransport(*giteaUrl)
Gitea = common.AllocateGiteaTransport(*giteaUrl)
config, err := common.ReadConfigFile(*workflowConfig)
if err != nil {
common.LogError("Cannot read config files:", err)
return
}
configs, err := common.ResolveWorkflowConfigs(gitea, config)
configs, err := common.ResolveWorkflowConfigs(Gitea, config)
if err != nil {
common.LogError("Cannot resolve config files:", err)
return
}
for _, c := range configs {
common.LogDebug(*c)
}
req := new(RequestProcessor)
req.configuredRepos = make(map[string][]*common.AutogitConfig)
@@ -111,26 +119,67 @@ func main() {
}
}
if CurrentUser, err = gitea.GetCurrentUser(); err != nil {
if CurrentUser, err = Gitea.GetCurrentUser(); err != nil {
common.LogError("Failed to fetch current gitea user:", err)
return
}
common.LogInfo("Running with token from", CurrentUser.UserName)
req.Synced = &PullRequestSynced{
gitea: gitea,
}
req.Opened = &PullRequestOpened{
gitea: gitea,
}
req.Closed = &PullRequestClosed{
gitea: gitea,
}
req.Review = &PullRequestReviewed{
gitea: gitea,
req.Synced = &PullRequestSynced{}
req.Opened = &PullRequestOpened{}
req.Closed = &PullRequestClosed{}
req.Review = &PullRequestReviewed{}
if *pr != "" {
rx := regexp.MustCompile("^([a-zA-Z0-9_\\.-]+)/([a-zA-Z0-9_\\.-]+)#([0-9]+)$")
if data := rx.FindStringSubmatch(*pr); data == nil {
common.LogError("Cannot parse PR line to process", *pr)
} else {
org := data[1]
repo := data[2]
num, err := strconv.ParseInt(data[3], 10, 64)
common.LogInfo("Processing:", org, "/", repo, "#", num)
common.PanicOnError(err)
pr, err := Gitea.GetPullRequest(org, repo, num)
if err != nil {
common.LogError("Cannot fetch PR", err)
return
}
if pr.State != "open" {
common.LogError("Can only deal with open PRs. This one is", pr.State)
return
}
c := configs.GetPrjGitConfig(org, repo, pr.Base.Ref)
if c == nil {
if pr.Base.Repo.DefaultBranch == pr.Base.Ref {
c = configs.GetPrjGitConfig(org, repo, "")
}
}
if c == nil {
common.LogError("Cannot find config for PR.")
return
}
git, err := GitHandler.CreateGitHandler(org)
common.PanicOnError(err)
defer git.Close()
p := common.PullRequestWebhookEvent{
Action: "opened",
Number: num,
Pull_Request: common.PullRequestFromModel(pr),
}
if err = req.Opened.Process(&p, git, c); err != nil {
common.LogError("processor returned error", err)
}
}
return
}
checker := CreateDefaultStateChecker(*checkOnStart, req, gitea, time.Duration(*checkIntervalHours)*time.Hour)
checker := CreateDefaultStateChecker(*checkOnStart, req, Gitea, time.Duration(*checkIntervalHours)*time.Hour)
go checker.ConsistencyCheckProcess()
listenDefs := common.ListenDefinitions{

View File

@@ -30,14 +30,26 @@ func (w *RequestProcessor) ProcessFunc(request *common.Request) error {
return nil
}
org := req.Pull_Request.Base.Repo.Owner.Username
pkg := req.Pull_Request.Base.Repo.Name
branch := req.Pull_Request.Base.Ref
assumed_git_project_name := org + "/" + pkg + "#" + branch
var config *common.AutogitConfig
for _, c := range configs {
if c.GitProjectName == req.Pull_Request.Base.Repo.Name ||
c.Branch == req.Pull_Request.Base.Ref {
if c.GitProjectName == assumed_git_project_name {
config = c
break
}
if c.Organization == org {
// default branch *or* match branch
if (c.Branch == "" && branch == req.Pull_Request.Base.Repo.Default_Branch) ||
(c.Branch != "" && c.Branch == branch) {
config = c
break
}
}
}
if config == nil {
return fmt.Errorf("Cannot find config for branch '%s'", req.Pull_Request.Base.Ref)

View File

@@ -4,9 +4,7 @@ import (
"src.opensuse.org/autogits/common"
)
type PullRequestClosed struct {
gitea common.Gitea
}
type PullRequestClosed struct{}
func (*PullRequestClosed) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
if req.Repository.Name != config.GitProjectName {

View File

@@ -5,50 +5,61 @@ import (
"src.opensuse.org/autogits/common"
)
type PullRequestOpened struct {
gitea common.Gitea
}
type PullRequestOpened struct{}
func (o *PullRequestOpened) CreateOrUpdatePrjGitPR(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
// create PrjGit branch for buidling the pull request
branchName := prGitBranchNameForPR(req)
// TODO: fix this for config.Organization
org, prj, _ := config.GetPrjGit()
prjGit, err := o.gitea.CreateRepositoryIfNotExist(git, org, prj)
common.PanicOnErrorWithMsg(err, "Error creating a prjgitrepo: "+err.Error())
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)
}
prOrg := req.Pull_Request.Base.Repo.Owner.Username
prRepo := req.Pull_Request.Base.Repo.Name
commitMsg := fmt.Sprintf(`auto-created for %s
This commit was autocreated by %s
referencing
`+common.PrPattern,
req.Repository.Owner.Username,
req.Repository.Name,
prOrg,
prRepo,
GitAuthor,
req.Repository.Name,
prRepo,
req.Pull_Request.Number,
)
subList, err := git.GitSubmoduleList(common.DefaultGitPrj, "HEAD")
common.PanicOnError(err)
if id := subList[req.Repository.Name]; id != req.Pull_Request.Head.Sha {
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 = o.gitea.CreatePullRequestIfNotExist(prjGit, branchName, prjGit.DefaultBranch,
fmt.Sprintf("Forwarded PR: %s", req.Repository.Name),
_, 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, req.Repository.Owner.Username, req.Repository.Name, req.Pull_Request.Number),
GitAuthor, prOrg, prRepo, req.Pull_Request.Number),
)
return err
@@ -57,27 +68,33 @@ referencing the following pull request:
func (o *PullRequestOpened) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
// requests against project are not handled here
common.LogInfo("processing opened PR:", req.Pull_Request.Url)
if org, repo, _ := config.GetPrjGit(); req.Repository.Owner.Username != org || req.Repository.Name != repo {
prOrg := req.Pull_Request.Base.Repo.Owner.Username
prRepo := req.Pull_Request.Base.Repo.Name
if prjGitOrg, prjGitRepo, _ := config.GetPrjGit(); prOrg != prjGitOrg || prRepo != prjGitRepo {
if err := o.CreateOrUpdatePrjGitPR(req, git, config); err != nil {
return err
}
}
prset, err := common.FetchPRSet(o.gitea, req.Repository.Owner.Username, req.Repository.Name, req.Number, config)
prset, err := common.FetchPRSet(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))
// 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))
maintainers, err := common.FetchProjectMaintainershipData(o.gitea, config.Organization, config.GitProjectName, config.Branch)
org, repo, branch := config.GetPrjGit()
maintainers, err := common.FetchProjectMaintainershipData(Gitea, org, repo, branch)
if err != nil {
return err
}
return prset.AssignReviewers(o.gitea, maintainers)
return prset.AssignReviewers(Gitea, maintainers)
}

View File

@@ -2,22 +2,20 @@ package main
import "src.opensuse.org/autogits/common"
type PullRequestReviewed struct {
gitea common.Gitea
}
type PullRequestReviewed struct{}
func (o *PullRequestReviewed) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
prset, err := common.FetchPRSet(o.gitea, req.Repository.Owner.Username, req.Repository.Name, req.Number, config)
prset, err := common.FetchPRSet(Gitea, req.Repository.Owner.Username, req.Repository.Name, req.Number, config)
if err != nil {
return err
}
maintainers, err := common.FetchProjectMaintainershipData(o.gitea, prset.Config.Organization, prset.Config.GitProjectName, prset.Config.Branch)
maintainers, err := common.FetchProjectMaintainershipData(Gitea, prset.Config.Organization, prset.Config.GitProjectName, prset.Config.Branch)
if err != nil {
return err
}
if prset.IsApproved(o.gitea, maintainers) {
if prset.IsApproved(Gitea, maintainers) {
return prset.Merge(GitHandler)
}

View File

@@ -10,7 +10,7 @@ import (
)
func prGitBranchNameForPR(req *common.PullRequestWebhookEvent) string {
return fmt.Sprintf("PR_%s#%d", req.Repository.Name, req.Pull_Request.Number)
return fmt.Sprintf("PR_%s#%d", req.Pull_Request.Base.Repo.Name, req.Pull_Request.Number)
}
func updateSubmoduleInPR(req *common.PullRequestWebhookEvent, git common.Git) {
@@ -25,18 +25,17 @@ func processPrjGitPullRequestSync(req *common.PullRequestWebhookEvent) error {
return nil
}
type PullRequestSynced struct {
gitea common.Gitea
}
type PullRequestSynced struct{}
func (o *PullRequestSynced) Process(req *common.PullRequestWebhookEvent, git common.Git, config *common.AutogitConfig) error {
if req.Repository.Name == config.GitProjectName {
prjGitOrg, prjGitRepo, _ := config.GetPrjGit()
if req.Repository.Owner.Username == prjGitOrg && req.Repository.Name == prjGitRepo {
return processPrjGitPullRequestSync(req)
}
// need to verify that submodule in the PR for prjgit
// is still pointing to the HEAD of the PR
pr, err := o.gitea.GetPullRequest(req.Repository.Owner.Username, req.Repository.Name, req.Number)
pr, err := Gitea.GetPullRequest(req.Repository.Owner.Username, req.Repository.Name, req.Number)
if err != nil {
return fmt.Errorf("Cannot fetch PR data from gitea: %w", err)
}
@@ -46,13 +45,15 @@ func (o *PullRequestSynced) Process(req *common.PullRequestWebhookEvent, git com
return fmt.Errorf("Package update associated with invalid number of projects. Expected 1. Got %d", len(prs))
}
prjPr, err := o.gitea.GetPullRequest(prs[0].Org, prs[0].Repo, prs[0].Num)
prjPr, err := Gitea.GetPullRequest(prs[0].Org, prs[0].Repo, prs[0].Num)
if err != nil {
return fmt.Errorf("Cannot get PrjGit PR in processPullRequestSync. Err: %w", err)
}
common.PanicOnError(git.GitExec("", "clone", "--branch", prjPr.Head.Name, "--depth", "1", prjPr.Head.Repo.SSHURL, common.DefaultGitPrj))
branchName := prGitBranchNameForPR(req)
remote, err := git.GitClone(common.DefaultGitPrj, branchName, prjPr.Head.Repo.SSHURL)
common.PanicOnError(err)
commitId, ok := git.GitSubmoduleCommitId(common.DefaultGitPrj, req.Repository.Name, prjPr.Head.Sha)
if !ok {
@@ -74,9 +75,8 @@ Update to %s`, req.Pull_Request.Head.Sha)
common.LogDebug("Creating new commit msg:", commitMsg)
// we need to update prjgit PR with the new head hash
branchName := prGitBranchNameForPR(req)
updateSubmoduleInPR(req, git)
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:"+branchName))
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", remote))
return nil
}

View File

@@ -26,14 +26,12 @@ type DefaultStateChecker struct {
checkOnStart bool
checkInterval time.Duration
gitea common.Gitea
processor *RequestProcessor
i StateChecker
}
func CreateDefaultStateChecker(checkOnStart bool, processor *RequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker {
var s = &DefaultStateChecker{
gitea: gitea,
checkInterval: interval,
checkOnStart: checkOnStart,
processor: processor,
@@ -62,8 +60,10 @@ func (s *DefaultStateChecker) ProcessPR(git common.Git, pr *models.PullRequest,
return fmt.Errorf("Unhandled pull request state: '%s'. %s/%s/%d", pr.State, config.Organization, "", pr.Index)
}
common.LogError(" * processor error returned:", err)
return nil
if err != nil {
common.LogError(" * processor error returned:", err)
}
return err
}
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) error {
@@ -80,20 +80,20 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) e
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
git, err := GitHandler.CreateGitHandler(prjGitOrg)
git, err := GitHandler.CreateGitHandler(config.Organization)
if err != nil {
return fmt.Errorf("Cannot create git handler: %w", err)
}
defer git.Close()
repo, err := s.gitea.CreateRepositoryIfNotExist(git, prjGitOrg, prjGitRepo)
repo, err := Gitea.CreateRepositoryIfNotExist(git, prjGitOrg, prjGitRepo)
if err != nil {
return fmt.Errorf("Error fetching or creating '%s/%s#%s' -- aborting verifyProjectState(). Err: %w", prjGitBranch, prjGitRepo, prjGitBranch, err)
}
_, err = git.GitClone(config.GitProjectName, prjGitBranch, repo.SSHURL)
common.PanicOnError(err)
prs, err := s.gitea.GetRecentPullRequests(prjGitOrg, prjGitRepo, prjGitBranch)
prs, err := Gitea.GetRecentPullRequests(prjGitOrg, prjGitRepo, prjGitBranch)
if err != nil {
return fmt.Errorf("Error fetching PrjGit Prs for %s/%s#%s: %w", prjGitOrg, prjGitRepo, prjGitBranch, err)
}
@@ -114,9 +114,18 @@ nextSubmodule:
}
// check if open PR have PR against project
prs, err := s.gitea.GetRecentPullRequests(config.Organization, submoduleName, config.Branch)
branch := config.Branch
if branch == "" {
repo, err := Gitea.GetRepository(config.Organization, submoduleName)
if err != nil || repo == nil {
common.LogError("Cannot fetch repository:", config.Organization, "/", submoduleName, "... submodule skipped.")
continue
}
branch = repo.DefaultBranch
}
prs, err := Gitea.GetRecentPullRequests(config.Organization, submoduleName, branch)
if err != nil {
return fmt.Errorf("Error fetching pull requests for %s/%s#%s. Err: %w", config.Organization, submoduleName, config.Branch, err)
return fmt.Errorf("Error fetching pull requests for %s/%s#%s. Err: %w", config.Organization, submoduleName, branch, err)
}
common.LogDebug(" - # of PRs to check:", len(prs))
@@ -125,7 +134,7 @@ nextSubmodule:
}
// check if the commited changes are syned with branches
commits, err := s.gitea.GetRecentCommits(config.Organization, submoduleName, config.Branch, 10)
commits, err := Gitea.GetRecentCommits(config.Organization, submoduleName, branch, 10)
if err != nil {
return fmt.Errorf("Error fetching recent commits for %s/%s. Err: %w", config.Organization, submoduleName, err)
}
@@ -143,18 +152,18 @@ nextSubmodule:
// not found in past, check if we should advance the branch label ... pull the submodule
git.GitExecOrPanic(config.GitProjectName, "submodule", "update", "--init", "--filter", "blob:none", "--", sub)
subDir := path.Join(config.GitProjectName, sub)
newCommits := common.SplitStringNoEmpty(git.GitExecWithOutputOrPanic(subDir, "rev-list", "^origin/"+config.Branch, commitID), "\n")
newCommits := common.SplitStringNoEmpty(git.GitExecWithOutputOrPanic(subDir, "rev-list", "^origin/"+branch, commitID), "\n")
if len(newCommits) >= 1 {
common.LogDebug(" - updating branch", config.Branch, "to new head", commitID, " - len:", len(newCommits))
git.GitExecOrPanic(subDir, "checkout", "-B", config.Branch, commitID)
common.LogDebug(" - updating branch", branch, "to new head", commitID, " - len:", len(newCommits))
git.GitExecOrPanic(subDir, "checkout", "-B", branch, commitID)
url := git.GitExecWithOutputOrPanic(subDir, "remote", "get-url", "origin", "--push")
sshUrl, err := common.TranslateHttpsToSshUrl(strings.TrimSpace(url))
if err != nil {
return fmt.Errorf("Cannot traslate HTTPS git URL to SSH_URL. %w", err)
}
git.GitExecOrPanic(subDir, "remote", "set-url", "origin", "--push", sshUrl)
git.GitExecOrPanic(subDir, "push", "origin", config.Branch)
git.GitExecOrPanic(subDir, "push", "origin", branch)
}
}