|
|
|
@@ -20,6 +20,7 @@ package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/xml"
|
|
|
|
|
"errors"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/url"
|
|
|
|
@@ -47,10 +48,10 @@ const (
|
|
|
|
|
var GiteaToken string
|
|
|
|
|
var runId uint
|
|
|
|
|
|
|
|
|
|
func fetchPrGit(git common.Git, pr *models.PullRequest) error {
|
|
|
|
|
func FetchPrGit(git common.Git, pr *models.PullRequest) error {
|
|
|
|
|
// clone PR head and base and return path
|
|
|
|
|
cloneURL := pr.Head.Repo.CloneURL
|
|
|
|
|
if giteaUseSshClone {
|
|
|
|
|
if GiteaUseSshClone {
|
|
|
|
|
cloneURL = pr.Head.Repo.SSHURL
|
|
|
|
|
}
|
|
|
|
|
if _, err := os.Stat(path.Join(git.GetPath(), pr.Head.Sha)); os.IsNotExist(err) {
|
|
|
|
@@ -63,7 +64,7 @@ func fetchPrGit(git common.Git, pr *models.PullRequest) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getObsProjectAssociatedWithPr(baseProject string, pr *models.PullRequest) string {
|
|
|
|
|
func GetObsProjectAssociatedWithPr(baseProject string, pr *models.PullRequest) string {
|
|
|
|
|
if pr.Base.Repo.Name == common.DefaultGitPrj {
|
|
|
|
|
// if default project git, we don't need it in project name to be unique
|
|
|
|
|
return fmt.Sprintf(
|
|
|
|
@@ -100,7 +101,7 @@ const (
|
|
|
|
|
BuildStatusSummaryUnknown = 4
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func processBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
|
|
|
|
|
func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
|
|
|
|
|
if _, finished := project.BuildResultSummary(); !finished {
|
|
|
|
|
return BuildStatusSummaryBuilding
|
|
|
|
|
}
|
|
|
|
@@ -171,7 +172,7 @@ func processBuildStatus(project, refProject *common.BuildResultList) BuildStatus
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
common.LogDebug(" found match for @ idx:", j)
|
|
|
|
|
res := processRepoBuildStatus(project.Result[i].Status, refProject.Result[j].Status)
|
|
|
|
|
res := ProcessRepoBuildStatus(project.Result[i].Status, refProject.Result[j].Status)
|
|
|
|
|
switch res {
|
|
|
|
|
case BuildStatusSummarySuccess:
|
|
|
|
|
break found
|
|
|
|
@@ -190,7 +191,7 @@ func processBuildStatus(project, refProject *common.BuildResultList) BuildStatus
|
|
|
|
|
return BuildStatusSummarySuccess
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func processRepoBuildStatus(results, ref []common.PackageBuildStatus) BuildStatusSummary {
|
|
|
|
|
func ProcessRepoBuildStatus(results, ref []common.PackageBuildStatus) BuildStatusSummary {
|
|
|
|
|
PackageBuildStatusSorter := func(a, b common.PackageBuildStatus) int {
|
|
|
|
|
return strings.Compare(a.Package, b.Package)
|
|
|
|
|
}
|
|
|
|
@@ -237,9 +238,9 @@ func processRepoBuildStatus(results, ref []common.PackageBuildStatus) BuildStatu
|
|
|
|
|
return BuildStatusSummarySuccess
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func generateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullRequest, obsClient *common.ObsClient, buildPrj string) (*common.ProjectMeta, error) {
|
|
|
|
|
func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullRequest, obsClient *common.ObsClient, buildPrj string) (*common.ProjectMeta, error) {
|
|
|
|
|
common.LogDebug("repo content fetching ...")
|
|
|
|
|
err := fetchPrGit(git, pr)
|
|
|
|
|
err := FetchPrGit(git, pr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("Cannot fetch PR git:", pr.URL)
|
|
|
|
|
return nil, err
|
|
|
|
@@ -272,12 +273,12 @@ func generateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque
|
|
|
|
|
// generate new project with paths pointinig back to original repos
|
|
|
|
|
// disable publishing
|
|
|
|
|
|
|
|
|
|
meta.Name = getObsProjectAssociatedWithPr(buildPrj, pr)
|
|
|
|
|
meta.Name = GetObsProjectAssociatedWithPr(buildPrj, pr)
|
|
|
|
|
meta.Description = fmt.Sprintf(`Pull request build job PR#%d to branch %s of %s/%s`,
|
|
|
|
|
pr.Index, pr.Base.Name, pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name)
|
|
|
|
|
meta.Url = fmt.Sprintf(
|
|
|
|
|
"%s/%s/%s/pulls/%d",
|
|
|
|
|
giteaUrl,
|
|
|
|
|
GiteaUrl,
|
|
|
|
|
url.PathEscape(pr.Base.Repo.Owner.UserName),
|
|
|
|
|
url.PathEscape(pr.Base.Repo.Name),
|
|
|
|
|
pr.Index,
|
|
|
|
@@ -308,7 +309,7 @@ func generateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque
|
|
|
|
|
// stagingProject:$buildProject
|
|
|
|
|
// ^- stagingProject:$buildProject:$subProjectName (based on templateProject)
|
|
|
|
|
|
|
|
|
|
func createQASubProject(git common.Git, gitea common.Gitea, pr *models.PullRequest, obsClient *common.ObsClient, buildProject, stagingProject, templateProject, subProjectName string) error {
|
|
|
|
|
func CreateQASubProject(git common.Git, gitea common.Gitea, pr *models.PullRequest, obsClient *common.ObsClient, buildProject, stagingProject, templateProject, subProjectName string) error {
|
|
|
|
|
common.LogDebug("Setup QA sub projects")
|
|
|
|
|
templateMeta, err := obsClient.GetProjectMeta(templateProject)
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -333,17 +334,22 @@ func createQASubProject(git common.Git, gitea common.Gitea, pr *models.PullReque
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = obsClient.SetProjectMeta(templateMeta)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("cannot create project:", templateMeta.Name, err)
|
|
|
|
|
return err
|
|
|
|
|
if !IsDryRun {
|
|
|
|
|
err = obsClient.SetProjectMeta(templateMeta)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("cannot create project:", templateMeta.Name, err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
common.LogDebug("Create project:", templateMeta.Name)
|
|
|
|
|
common.LogDebug(templateMeta)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func startOrUpdateBuild(git common.Git, gitea common.Gitea, pr *models.PullRequest, obsClient *common.ObsClient, baseProject string) (RequestModification, error) {
|
|
|
|
|
func StartOrUpdateBuild(git common.Git, gitea common.Gitea, pr *models.PullRequest, obsClient *common.ObsClient, baseProject string) (RequestModification, error) {
|
|
|
|
|
common.LogDebug("fetching OBS project Meta")
|
|
|
|
|
obsPrProject := getObsProjectAssociatedWithPr(baseProject, pr)
|
|
|
|
|
obsPrProject := GetObsProjectAssociatedWithPr(baseProject, pr)
|
|
|
|
|
meta, err := obsClient.GetProjectMeta(obsPrProject)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("error fetching project meta for", obsPrProject, ":", err)
|
|
|
|
@@ -368,15 +374,16 @@ func startOrUpdateBuild(git common.Git, gitea common.Gitea, pr *models.PullReque
|
|
|
|
|
var state RequestModification = RequestModificationSourceChanged
|
|
|
|
|
if meta == nil {
|
|
|
|
|
// new build
|
|
|
|
|
meta, err = generateObsPrjMeta(git, gitea, pr, obsClient, baseProject)
|
|
|
|
|
meta, err = GenerateObsPrjMeta(git, gitea, pr, obsClient, baseProject)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return RequestModificationNoChange, err
|
|
|
|
|
}
|
|
|
|
|
state = RequestModificationProjectCreated
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if dryRun {
|
|
|
|
|
if IsDryRun {
|
|
|
|
|
x, _ := xml.MarshalIndent(meta, "", " ")
|
|
|
|
|
common.LogDebug("Creating build project:")
|
|
|
|
|
common.LogDebug(" meta:", x)
|
|
|
|
|
} else {
|
|
|
|
|
err = obsClient.SetProjectMeta(meta)
|
|
|
|
@@ -389,7 +396,51 @@ func startOrUpdateBuild(git common.Git, gitea common.Gitea, pr *models.PullReque
|
|
|
|
|
return state, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func processPullNotification(gitea common.Gitea, thread *models.NotificationThread) {
|
|
|
|
|
func IsReviewerRequested(pr *models.PullRequest) bool {
|
|
|
|
|
for _, reviewer := range pr.RequestedReviewers {
|
|
|
|
|
if reviewer.UserName == Username {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var NonActionableReviewError = errors.New("Ignoring non-actionable review request. Marking as done.")
|
|
|
|
|
var NoReviewsFoundError = errors.New("No review requests found for the review user. Marking as done.")
|
|
|
|
|
|
|
|
|
|
func FetchOurLatestActionableReview(gitea common.Gitea, org, repo string, id int64) (*models.PullReview, error) {
|
|
|
|
|
reviews, err := gitea.GetPullRequestReviews(org, repo, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slices.SortFunc(reviews, func(a, b *models.PullReview) int {
|
|
|
|
|
return time.Time(a.Submitted).Compare(time.Time(b.Submitted))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
for idx := len(reviews) - 1; idx >= 0; idx-- {
|
|
|
|
|
review := reviews[idx]
|
|
|
|
|
if review.User != nil || review.User.UserName == Username {
|
|
|
|
|
if IsDryRun {
|
|
|
|
|
// for purposes of moving forward a no-op check
|
|
|
|
|
return review, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch review.State {
|
|
|
|
|
default:
|
|
|
|
|
// non-actionable state, mark as done
|
|
|
|
|
common.LogInfo("Ignoring non-actionable review request. Marking as done.")
|
|
|
|
|
return nil, NonActionableReviewError
|
|
|
|
|
case common.ReviewStatePending, common.ReviewStateRequestReview:
|
|
|
|
|
return review, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil, NoReviewsFoundError
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ProcessPullNotification(gitea common.Gitea, thread *models.NotificationThread) {
|
|
|
|
|
defer func() {
|
|
|
|
|
err := recover()
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -398,12 +449,6 @@ func processPullNotification(gitea common.Gitea, thread *models.NotificationThre
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
dir, err := os.MkdirTemp(os.TempDir(), BotName)
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
gh, err := common.AllocateGitWorkTree(dir, GitAuthor, "noaddress@suse.de")
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
|
|
|
|
|
rx := regexp.MustCompile(`^https://src\.(?:open)?suse\.(?:org|de)/api/v\d+/repos/(?<org>[-_a-zA-Z0-9]+)/(?<project>[-_a-zA-Z0-9]+)/issues/(?<num>[0-9]+)$`)
|
|
|
|
|
notification := thread.Subject
|
|
|
|
|
match := rx.FindStringSubmatch(notification.URL)
|
|
|
|
@@ -421,155 +466,135 @@ func processPullNotification(gitea common.Gitea, thread *models.NotificationThre
|
|
|
|
|
repo := match[2]
|
|
|
|
|
id, _ := strconv.ParseInt(match[3], 10, 64)
|
|
|
|
|
|
|
|
|
|
err := ProcessPullRequest(gitea, org, repo, id)
|
|
|
|
|
if !IsDryRun && err == nil {
|
|
|
|
|
gitea.SetNotificationRead(thread.ID)
|
|
|
|
|
} else if err != nil {
|
|
|
|
|
common.LogError(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) error {
|
|
|
|
|
dir, err := os.MkdirTemp(os.TempDir(), BotName)
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
|
|
|
|
gh, err := common.AllocateGitWorkTree(dir, GitAuthor, "noaddress@suse.de")
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
|
|
|
|
|
git, err := gh.CreateGitHandler(org)
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
|
|
|
|
|
pr, err := gitea.GetPullRequest(org, repo, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("No PR associated with review:", notification.URL, "Error:", err)
|
|
|
|
|
return
|
|
|
|
|
common.LogError("No PR associated with review:", org, "/", repo, "#", id, "Error:", err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
common.LogDebug("PR state:", pr.State)
|
|
|
|
|
if pr.State == "closed" {
|
|
|
|
|
// dismiss the review
|
|
|
|
|
common.LogInfo(" -- closed request, so nothing to review")
|
|
|
|
|
gitea.SetNotificationRead(thread.ID)
|
|
|
|
|
return
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reviews, err := gitea.GetPullRequestReviews(org, repo, id)
|
|
|
|
|
obsClient, err := common.NewObsClient(ObsApiHost)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogInfo("No reviews associated with request:", notification.URL, "Error:", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
obsClient, err := common.NewObsClient(obsApiHost)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError(err)
|
|
|
|
|
return
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(BuildRoot) > 0 {
|
|
|
|
|
obsClient.HomeProject = BuildRoot
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reviewRequested := false
|
|
|
|
|
for _, reviewer := range pr.RequestedReviewers {
|
|
|
|
|
if reviewer.UserName == Username {
|
|
|
|
|
reviewRequested = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !reviewRequested {
|
|
|
|
|
if !IsReviewerRequested(pr) && !IsDryRun {
|
|
|
|
|
common.LogError("Review not requested in notification. Setting to status 'read'")
|
|
|
|
|
gitea.SetNotificationRead(thread.ID)
|
|
|
|
|
return
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newReviews := make([]*models.PullReview, 0, len(reviews))
|
|
|
|
|
for _, review := range reviews {
|
|
|
|
|
if review.User != nil && (review.User.UserName == Username) {
|
|
|
|
|
newReviews = append(newReviews, review)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reviews = newReviews
|
|
|
|
|
if review, err := FetchOurLatestActionableReview(gitea, org, repo, id); err == nil {
|
|
|
|
|
common.LogInfo("processing review", review.HTMLURL, "state", review.State)
|
|
|
|
|
|
|
|
|
|
slices.SortFunc(reviews, func(a, b *models.PullReview) int {
|
|
|
|
|
return time.Time(a.Submitted).Compare(time.Time(b.Submitted))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// find review request or review in progress...
|
|
|
|
|
for idx := len(reviews) - 1; idx >= 0; idx-- {
|
|
|
|
|
review := reviews[idx]
|
|
|
|
|
if review.User.UserName != Username {
|
|
|
|
|
continue
|
|
|
|
|
err = FetchPrGit(git, pr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("Cannot fetch PR git:", pr.URL)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
common.LogInfo("processing review", idx, "state", review.State)
|
|
|
|
|
// find modified submodules and new submodules -- build them
|
|
|
|
|
dir := pr.Head.Sha
|
|
|
|
|
headSubmodules, err := git.GitSubmoduleList(dir, pr.Head.Sha)
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
baseSubmodules, err := git.GitSubmoduleList(dir, pr.Base.Sha)
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
|
|
|
|
|
switch review.State {
|
|
|
|
|
modifiedOrNew := make([]string, 0, 16)
|
|
|
|
|
for pkg, headOid := range headSubmodules {
|
|
|
|
|
if baseOid, exists := baseSubmodules[pkg]; !exists || baseOid != headOid {
|
|
|
|
|
modifiedOrNew = append(modifiedOrNew, pkg)
|
|
|
|
|
common.LogDebug(pkg, "modified:", baseOid, "->", headOid)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create build project, if doesn't exist, and add it to pending requests
|
|
|
|
|
case common.ReviewStateUnknown:
|
|
|
|
|
// what to do?
|
|
|
|
|
|
|
|
|
|
case common.ReviewStatePending, common.ReviewStateRequestReview:
|
|
|
|
|
err = fetchPrGit(git, pr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("Cannot fetch PR git:", pr.URL)
|
|
|
|
|
return
|
|
|
|
|
// we want the possibly pending modification here, in case stagings are added, etc.
|
|
|
|
|
// jobs of review team to deal with issues
|
|
|
|
|
common.LogDebug("QA configuration fetching ...")
|
|
|
|
|
buildProject := ""
|
|
|
|
|
QA := []common.QAConfig{}
|
|
|
|
|
if stagingConfig, err := common.ReadWorkflowConfig(gitea, fmt.Sprintf("%s/%s#%s", pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Head.Sha)); err == nil {
|
|
|
|
|
if len(stagingConfig.ObsBuildProject) > 1 {
|
|
|
|
|
buildProject = stagingConfig.ObsBuildProject
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find modified submodules and new submodules -- build them
|
|
|
|
|
dir := pr.Head.Sha
|
|
|
|
|
headSubmodules, err := git.GitSubmoduleList(dir, pr.Head.Sha)
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
baseSubmodules, err := git.GitSubmoduleList(dir, pr.Base.Sha)
|
|
|
|
|
common.PanicOnError(err)
|
|
|
|
|
QA = stagingConfig.QA
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modifiedOrNew := make([]string, 0, 16)
|
|
|
|
|
for pkg, headOid := range headSubmodules {
|
|
|
|
|
if baseOid, exists := baseSubmodules[pkg]; !exists || baseOid != headOid {
|
|
|
|
|
modifiedOrNew = append(modifiedOrNew, pkg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we want the possibly pending modification here, in case stagings are added, etc.
|
|
|
|
|
// jobs of review team to deal with issues
|
|
|
|
|
common.LogDebug("QA configuration fetching ...")
|
|
|
|
|
buildProject := ""
|
|
|
|
|
QA := []common.QAConfig{}
|
|
|
|
|
if stagingConfig, err := common.ReadWorkflowConfig(gitea, fmt.Sprintf("%s/%s#%s", pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Head.Sha)); err == nil {
|
|
|
|
|
if len(stagingConfig.ObsBuildProject) > 1 {
|
|
|
|
|
buildProject = stagingConfig.ObsBuildProject
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QA = stagingConfig.QA
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(buildProject) < 1 {
|
|
|
|
|
if len(buildProject) < 1 {
|
|
|
|
|
if !IsDryRun {
|
|
|
|
|
_, err := gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot find reference project")
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError(err)
|
|
|
|
|
return
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
common.LogError("Cannot find reference project for %s PR#%d\n", pr.Base.Name, pr.Index)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
common.LogError("Cannot find reference project for %s PR#%d\n", pr.Base.Name, pr.Index)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stagingProject := getObsProjectAssociatedWithPr(buildProject, pr)
|
|
|
|
|
change, err := startOrUpdateBuild(git, gitea, pr, obsClient, buildProject)
|
|
|
|
|
if change != RequestModificationNoChange {
|
|
|
|
|
msg := "Changed source updated for build"
|
|
|
|
|
if change == RequestModificationProjectCreated {
|
|
|
|
|
msg = "Build is started in https://" + obsWebHost + "/project/show/" +
|
|
|
|
|
stagingProject
|
|
|
|
|
}
|
|
|
|
|
stagingProject := GetObsProjectAssociatedWithPr(buildProject, pr)
|
|
|
|
|
change, err := StartOrUpdateBuild(git, gitea, pr, obsClient, buildProject)
|
|
|
|
|
if change != RequestModificationNoChange {
|
|
|
|
|
msg := "Changed source updated for build"
|
|
|
|
|
if change == RequestModificationProjectCreated {
|
|
|
|
|
msg = "Build is started in https://" + ObsWebHost + "/project/show/" +
|
|
|
|
|
stagingProject
|
|
|
|
|
}
|
|
|
|
|
if !IsDryRun {
|
|
|
|
|
gitea.AddComment(pr, msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if change == RequestModificationProjectCreated {
|
|
|
|
|
for _, setup := range QA {
|
|
|
|
|
createQASubProject(git, gitea, pr, obsClient,
|
|
|
|
|
buildProject,
|
|
|
|
|
stagingProject,
|
|
|
|
|
setup.Origin,
|
|
|
|
|
setup.Name)
|
|
|
|
|
}
|
|
|
|
|
if change == RequestModificationProjectCreated {
|
|
|
|
|
for _, setup := range QA {
|
|
|
|
|
CreateQASubProject(git, gitea, pr, obsClient,
|
|
|
|
|
buildProject,
|
|
|
|
|
stagingProject,
|
|
|
|
|
setup.Origin,
|
|
|
|
|
setup.Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
baseResult, err := obsClient.BuildStatus(buildProject, modifiedOrNew...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("failed fetching ref project status for", buildProject, ":", err)
|
|
|
|
|
}
|
|
|
|
|
stagingResult, err := obsClient.BuildStatus(stagingProject)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("failed fetching ref project status for", stagingProject, ":", err)
|
|
|
|
|
}
|
|
|
|
|
buildStatus := processBuildStatus(stagingResult, baseResult)
|
|
|
|
|
baseResult, err := obsClient.LastBuildResults(buildProject, modifiedOrNew...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("failed fetching ref project status for", buildProject, ":", err)
|
|
|
|
|
}
|
|
|
|
|
stagingResult, err := obsClient.BuildStatus(stagingProject)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.LogError("failed fetching ref project status for", stagingProject, ":", err)
|
|
|
|
|
}
|
|
|
|
|
buildStatus := ProcessBuildStatus(stagingResult, baseResult)
|
|
|
|
|
|
|
|
|
|
if !IsDryRun {
|
|
|
|
|
switch buildStatus {
|
|
|
|
|
case BuildStatusSummarySuccess:
|
|
|
|
|
_, err := gitea.AddReviewComment(pr, common.ReviewStateApproved, "Build successful")
|
|
|
|
@@ -582,29 +607,18 @@ func processPullNotification(gitea common.Gitea, thread *models.NotificationThre
|
|
|
|
|
common.LogError(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
common.LogInfo("Build status waiting:", buildStatus)
|
|
|
|
|
// waiting for build results -- nothing to do
|
|
|
|
|
|
|
|
|
|
case common.ReviewStateApproved:
|
|
|
|
|
// done, mark notification as read
|
|
|
|
|
common.LogInfo("processing request for success build ...")
|
|
|
|
|
if !dryRun {
|
|
|
|
|
gitea.SetNotificationRead(thread.ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case common.ReviewStateRequestChanges:
|
|
|
|
|
// build failures, nothing to do here, mark notification as read
|
|
|
|
|
common.LogInfo("processing request for failed request changes...")
|
|
|
|
|
if !dryRun {
|
|
|
|
|
gitea.SetNotificationRead(thread.ID)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
common.LogInfo("Build status:", buildStatus)
|
|
|
|
|
// waiting for build results -- nothing to do
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
} else if err == NonActionableReviewError || err == NoReviewsFoundError {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func pollWorkNotifications(giteaUrl string) {
|
|
|
|
|
func PollWorkNotifications(giteaUrl string) {
|
|
|
|
|
gitea := common.AllocateGiteaTransport(giteaUrl)
|
|
|
|
|
data, err := gitea.GetPullNotifications(nil)
|
|
|
|
|
|
|
|
|
@@ -618,12 +632,14 @@ func pollWorkNotifications(giteaUrl string) {
|
|
|
|
|
for _, notification := range data {
|
|
|
|
|
common.LogInfo(notification.ID, "--", notification.Subject)
|
|
|
|
|
|
|
|
|
|
if !ListPullNotificationsOnly && (ProcessIDOnly < 0 || ProcessIDOnly == notification.ID) {
|
|
|
|
|
if !ListPullNotificationsOnly {
|
|
|
|
|
switch notification.Subject.Type {
|
|
|
|
|
case "Pull":
|
|
|
|
|
processPullNotification(gitea, notification)
|
|
|
|
|
ProcessPullNotification(gitea, notification)
|
|
|
|
|
default:
|
|
|
|
|
gitea.SetNotificationRead(notification.ID)
|
|
|
|
|
if !IsDryRun {
|
|
|
|
|
gitea.SetNotificationRead(notification.ID)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -631,13 +647,13 @@ func pollWorkNotifications(giteaUrl string) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ListPullNotificationsOnly bool
|
|
|
|
|
var ProcessIDOnly int64
|
|
|
|
|
var BuildRoot string
|
|
|
|
|
var giteaUrl string
|
|
|
|
|
var giteaUseSshClone bool
|
|
|
|
|
var obsApiHost string
|
|
|
|
|
var obsWebHost string
|
|
|
|
|
var dryRun bool
|
|
|
|
|
var GiteaUrl string
|
|
|
|
|
var GiteaUseSshClone bool
|
|
|
|
|
var ObsApiHost string
|
|
|
|
|
var ObsWebHost string
|
|
|
|
|
var IsDryRun bool
|
|
|
|
|
var ProcessPROnly string
|
|
|
|
|
|
|
|
|
|
func ObsWebHostFromApiHost(apihost string) string {
|
|
|
|
|
if len(apihost) > 4 && apihost[0:4] == "api." {
|
|
|
|
@@ -649,13 +665,13 @@ func ObsWebHostFromApiHost(apihost string) string {
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
flag.BoolVar(&ListPullNotificationsOnly, "list-notifications-only", false, "Only lists notifications without acting on them")
|
|
|
|
|
flag.Int64Var(&ProcessIDOnly, "id", -1, "Process only the specific ID and ignore the rest. Use for debugging")
|
|
|
|
|
ProcessPROnly := flag.String("pr", "", "Process only specific PR and ignore the rest. Use for debugging")
|
|
|
|
|
flag.StringVar(&BuildRoot, "build-root", "", "Default build location for staging projects. Default is bot's home project")
|
|
|
|
|
flag.StringVar(&giteaUrl, "gitea-url", "https://src.opensuse.org", "Gitea instance")
|
|
|
|
|
flag.BoolVar(&giteaUseSshClone, "use-ssh-clone", false, "enforce cloning via ssh")
|
|
|
|
|
flag.StringVar(&obsApiHost, "obs", "api.opensuse.org", "API for OBS instance")
|
|
|
|
|
flag.StringVar(&obsWebHost, "obs-web", "", "Web OBS instance, if not derived from the obs config")
|
|
|
|
|
flag.BoolVar(&dryRun, "dry", false, "Dry-run, don't actually create any build projects or review changes")
|
|
|
|
|
flag.StringVar(&GiteaUrl, "gitea-url", "https://src.opensuse.org", "Gitea instance")
|
|
|
|
|
flag.BoolVar(&GiteaUseSshClone, "use-ssh-clone", false, "enforce cloning via ssh")
|
|
|
|
|
flag.StringVar(&ObsApiHost, "obs", "api.opensuse.org", "API for OBS instance")
|
|
|
|
|
flag.StringVar(&ObsWebHost, "obs-web", "", "Web OBS instance, if not derived from the obs config")
|
|
|
|
|
flag.BoolVar(&IsDryRun, "dry", false, "Dry-run, don't actually create any build projects or review changes")
|
|
|
|
|
debug := flag.Bool("debug", false, "One-shot run. Use for debugging")
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
|
@@ -664,15 +680,30 @@ func main() {
|
|
|
|
|
}
|
|
|
|
|
common.SetLoggingLevel(common.LogLevelInfo)
|
|
|
|
|
|
|
|
|
|
if len(obsWebHost) == 0 {
|
|
|
|
|
obsWebHost = ObsWebHostFromApiHost(obsApiHost)
|
|
|
|
|
if len(ObsWebHost) == 0 {
|
|
|
|
|
ObsWebHost = ObsWebHostFromApiHost(ObsApiHost)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
common.PanicOnErrorWithMsg(common.RequireGiteaSecretToken(), "Cannot find GITEA_TOKEN")
|
|
|
|
|
common.PanicOnErrorWithMsg(common.RequireObsSecretToken(), "Cannot find OBS_USER and OBS_PASSWORD")
|
|
|
|
|
|
|
|
|
|
if len(*ProcessPROnly) > 0 {
|
|
|
|
|
rx := regexp.MustCompile("^(\\w+)/(\\w+)#(\\d+)$")
|
|
|
|
|
m := rx.FindStringSubmatch(*ProcessPROnly)
|
|
|
|
|
if m == nil {
|
|
|
|
|
common.LogError("Cannot find any PR matches in", *ProcessPROnly)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gitea := common.AllocateGiteaTransport(GiteaUrl)
|
|
|
|
|
id, _ := strconv.ParseInt(m[3], 10, 64)
|
|
|
|
|
|
|
|
|
|
ProcessPullRequest(gitea, m[1], m[2], id)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
pollWorkNotifications(giteaUrl)
|
|
|
|
|
PollWorkNotifications(GiteaUrl)
|
|
|
|
|
if *debug {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|