.
This commit is contained in:
parent
f48a5b62f5
commit
641885a2d7
@ -8,6 +8,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ObsClient struct {
|
type ObsClient struct {
|
||||||
@ -179,7 +180,7 @@ func (c *ObsClient) DeleteProject(project string) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BuildStatus struct {
|
type PackageBuildStatus struct {
|
||||||
Package string `xml:"package,attr"`
|
Package string `xml:"package,attr"`
|
||||||
Code string `xml:"code,attr"`
|
Code string `xml:"code,attr"`
|
||||||
Details string `xml:"details"`
|
Details string `xml:"details"`
|
||||||
@ -190,7 +191,8 @@ type BuildResult struct {
|
|||||||
Repository string `xml:"repository,attr"`
|
Repository string `xml:"repository,attr"`
|
||||||
Arch string `xml:"arch,attr"`
|
Arch string `xml:"arch,attr"`
|
||||||
Code string `xml:"code,attr"`
|
Code string `xml:"code,attr"`
|
||||||
Status []BuildStatus `xml:"status"`
|
Dirty bool `xml:"dirty,attr"`
|
||||||
|
Status []PackageBuildStatus `xml:"status"`
|
||||||
Binaries []BinaryList `xml:"binarylist"`
|
Binaries []BinaryList `xml:"binarylist"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,6 +212,193 @@ type BuildResultList struct {
|
|||||||
Result []BuildResult `xml:"result"`
|
Result []BuildResult `xml:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *BuildResultList) GetPackageList() []string {
|
||||||
|
pkgList := make([]string, 0, 16)
|
||||||
|
|
||||||
|
for _, res := range r.Result {
|
||||||
|
// TODO: enough to iterate over one result set?
|
||||||
|
|
||||||
|
for _, status := range res.Status {
|
||||||
|
if !slices.Contains(pkgList, status.Package) {
|
||||||
|
pkgList = append(pkgList, status.Package)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BuildResultList) BuildResultSummary() (success, finished bool) {
|
||||||
|
finished = len(r.Result) > 0 && len(r.Result[0].Status) > 0
|
||||||
|
success = finished
|
||||||
|
|
||||||
|
for _, resultSet := range r.Result {
|
||||||
|
repoDetail, ok := ObsRepoStatusDetails[resultSet.Code]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
panic("Unknown repo result code: " + resultSet.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
finished = repoDetail.Finished
|
||||||
|
if !finished || resultSet.Dirty {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, result := range resultSet.Status {
|
||||||
|
detail, ok := ObsBuildStatusDetails[result.Code]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
panic("Unknown result code: " + result.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
finished = finished && detail.Finished
|
||||||
|
success = success && detail.Success
|
||||||
|
|
||||||
|
if !finished {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var ObsBuildStatusDetails map[string]ObsBuildStatusDetail
|
||||||
|
var ObsRepoStatusDetails map[string]ObsBuildStatusDetail
|
||||||
|
|
||||||
|
type ObsBuildStatusDetail struct {
|
||||||
|
Code string
|
||||||
|
Description string
|
||||||
|
Finished bool
|
||||||
|
Success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ObsBuildStatusDetails = make(map[string]ObsBuildStatusDetail)
|
||||||
|
ObsRepoStatusDetails = make(map[string]ObsBuildStatusDetail)
|
||||||
|
|
||||||
|
// package status
|
||||||
|
ObsBuildStatusDetails["succeeded"] = ObsBuildStatusDetail{
|
||||||
|
Code: "succeeded",
|
||||||
|
Description: "Package has built successfully and can be used to build further packages.",
|
||||||
|
Finished: true,
|
||||||
|
Success: true,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["failed"] = ObsBuildStatusDetail{
|
||||||
|
Code: "failed",
|
||||||
|
Description: "The package does not build successfully. No packages have been created. Packages that depend on this package will be built using any previously created packages, if they exist.",
|
||||||
|
Finished: true,
|
||||||
|
Success: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["unresolvable"] = ObsBuildStatusDetail{
|
||||||
|
Code: "unresolvable",
|
||||||
|
Description: "The build can not begin, because required packages are either missing or not explicitly defined.",
|
||||||
|
Finished: true,
|
||||||
|
Success: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["broken"] = ObsBuildStatusDetail{
|
||||||
|
Code: "broken",
|
||||||
|
Description: "The sources either contain no build description (e.g. specfile), automatic source processing failed or a merge conflict does exist.",
|
||||||
|
Finished: true,
|
||||||
|
Success: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["blocked"] = ObsBuildStatusDetail{
|
||||||
|
Code: "blocked",
|
||||||
|
Description: "This package waits for other packages to be built. These can be in the same or other projects.",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["scheduled"] = ObsBuildStatusDetail{
|
||||||
|
Code: "scheduled",
|
||||||
|
Description: "A package has been marked for building, but the build has not started yet.",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["dispatching"] = ObsBuildStatusDetail{
|
||||||
|
Code: "dispatching",
|
||||||
|
Description: "A package is being copied to a build host. This is an intermediate state before building.",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["building"] = ObsBuildStatusDetail{
|
||||||
|
Code: "building",
|
||||||
|
Description: "The package is currently being built.",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["signing"] = ObsBuildStatusDetail{
|
||||||
|
Code: "signing",
|
||||||
|
Description: "The package has been built successfully and is assigned to get signed.",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["finished"] = ObsBuildStatusDetail{
|
||||||
|
Code: "finished",
|
||||||
|
Description: "The package has been built and signed, but has not yet been picked up by the scheduler. This is an intermediate state prior to 'succeeded' or 'failed'.",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["disabled"] = ObsBuildStatusDetail{
|
||||||
|
Code: "disabled",
|
||||||
|
Description: "The package has been disabled from building in project or package metadata. Packages that depend on this package will be built using any previously created packages, if they still exist.",
|
||||||
|
Finished: true,
|
||||||
|
Success: true,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["excluded"] = ObsBuildStatusDetail{
|
||||||
|
Code: "excluded",
|
||||||
|
Description: "The package build has been disabled in package build description (for example in the .spec file) or does not provide a matching build description for the target.",
|
||||||
|
Finished: true,
|
||||||
|
Success: true,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["locked"] = ObsBuildStatusDetail{
|
||||||
|
Code: "locked",
|
||||||
|
Description: "The package is frozen",
|
||||||
|
Finished: true,
|
||||||
|
Success: true,
|
||||||
|
}
|
||||||
|
ObsBuildStatusDetails["unknown"] = ObsBuildStatusDetail{
|
||||||
|
Code: "unknown",
|
||||||
|
Description: "The scheduler has not yet evaluated this package. Should be a short intermediate state for new packages.",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// repo status
|
||||||
|
ObsRepoStatusDetails["published"] = ObsBuildStatusDetail{
|
||||||
|
Code: "published",
|
||||||
|
Description: "Repository has been published",
|
||||||
|
Finished: true,
|
||||||
|
}
|
||||||
|
ObsRepoStatusDetails["publishing"] = ObsBuildStatusDetail{
|
||||||
|
Code: "publishing",
|
||||||
|
Description: "Repository is being created right now",
|
||||||
|
Finished: true,
|
||||||
|
}
|
||||||
|
ObsRepoStatusDetails["unpublished"] = ObsBuildStatusDetail{
|
||||||
|
Code: "unpublished",
|
||||||
|
Description: "Build finished, but repository publishing is disabled",
|
||||||
|
Finished: true,
|
||||||
|
}
|
||||||
|
ObsRepoStatusDetails["building"] = ObsBuildStatusDetail{
|
||||||
|
Code: "building",
|
||||||
|
Description: "Build jobs exist for the repository",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
ObsRepoStatusDetails["finished"] = ObsBuildStatusDetail{
|
||||||
|
Code: "finished",
|
||||||
|
Description: "Build jobs have been processed, new repository is not yet created",
|
||||||
|
Finished: true,
|
||||||
|
}
|
||||||
|
ObsRepoStatusDetails["blocked"] = ObsBuildStatusDetail{
|
||||||
|
Code: "blocked",
|
||||||
|
Description: "No build possible at the moment, waiting for jobs in other repositories",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
ObsRepoStatusDetails["broken"] = ObsBuildStatusDetail{
|
||||||
|
Code: "broken",
|
||||||
|
Description: "The repository setup is broken, build or publish not possible",
|
||||||
|
Finished: true,
|
||||||
|
}
|
||||||
|
ObsRepoStatusDetails["scheduling"] = ObsBuildStatusDetail{
|
||||||
|
Code: "scheduling",
|
||||||
|
Description: "The repository state is being calculated right now",
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func parseBuildResults(data []byte) (*BuildResultList, error) {
|
func parseBuildResults(data []byte) (*BuildResultList, error) {
|
||||||
result := BuildResultList{}
|
result := BuildResultList{}
|
||||||
err := xml.Unmarshal(data, &result)
|
err := xml.Unmarshal(data, &result)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -60,35 +61,50 @@ func getObsProjectAssociatedWithPr(baseProject string, pr *models.PullRequest) s
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BuildStatusSummary string
|
|
||||||
|
|
||||||
const BuildUnresolveable = BuildStatusSummary("unresolveable")
|
|
||||||
const BuildBuilding = BuildStatusSummary("building")
|
|
||||||
const BuildFailed = BuildStatusSummary("failed")
|
|
||||||
const BuildSuccess = BuildStatusSummary("success")
|
|
||||||
|
|
||||||
/*
|
|
||||||
'published' => 'Repository has been published',
|
|
||||||
'publishing' => 'Repository is being created right now',
|
|
||||||
'unpublished' => 'Build finished, but repository publishing is disabled',
|
|
||||||
'building' => 'Build jobs exist for the repository',
|
|
||||||
'finished' => 'Build jobs have been processed, new repository is not yet created',
|
|
||||||
'blocked' => 'No build possible at the moment, waiting for jobs in other repositories',
|
|
||||||
'broken' => 'The repository setup is broken, build or publish not possible',
|
|
||||||
'scheduling' => 'The repository state is being calculated right now'
|
|
||||||
*/
|
|
||||||
type BuildStatus struct {
|
|
||||||
Status BuildStatusSummary
|
|
||||||
Detail string
|
|
||||||
}
|
|
||||||
|
|
||||||
var buildStatus map[string]BuildStatus
|
|
||||||
|
|
||||||
func processBuildStatusUpdate() {
|
func processBuildStatusUpdate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func processBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
|
type BuildStatusSummary int
|
||||||
return BuildBuilding
|
|
||||||
|
const (
|
||||||
|
BuildStatusSummarySuccess = 1
|
||||||
|
BuildStatusSummaryFailed = 2
|
||||||
|
BuildStatusSummaryBuilding = 3
|
||||||
|
BuildStatusSummaryUnknown = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
func processBuildStatus(h *common.RequestHandler, project, refProject *common.BuildResultList) BuildStatusSummary {
|
||||||
|
targetResults := project.Result
|
||||||
|
|
||||||
|
if _, finished := project.BuildResultSummary(); !finished {
|
||||||
|
return BuildStatusSummaryBuilding
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, finished := refProject.BuildResultSummary(); !finished {
|
||||||
|
h.LogError("refProject not finished building??")
|
||||||
|
return BuildStatusSummaryUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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`
|
||||||
|
BuildResultSorter := func(a, b common.BuildResult) int {
|
||||||
|
if c := strings.Compare(a.Project, b.Project); c != 0 {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
if c := strings.Compare(a.Repository, b.Repository); c != 0 {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
if c := strings.Compare(a.Arch, b.Arch); c != 0 {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("Should not happen -- BuiltResultSorter equal repos?")
|
||||||
|
}
|
||||||
|
slices.SortFunc(project.Result, BuildResultSorter)
|
||||||
|
slices.SortFunc(refProject.Result, BuildResultSorter)
|
||||||
|
|
||||||
|
return BuildStatusSummaryUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func startBuild(h *common.RequestHandler, pr *models.PullRequest, obsClient *common.ObsClient) error {
|
func startBuild(h *common.RequestHandler, pr *models.PullRequest, obsClient *common.ObsClient) error {
|
||||||
@ -267,16 +283,19 @@ func processPullNotification(h *common.RequestHandler, thread *models.Notificati
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.Log("repo content fetching ...")
|
h.Log("repo content fetching ...")
|
||||||
refProject := string(h.GitCatFile(dir, pr.Head.Sha, "project.build"))
|
refPrj := string(bytes.TrimSpace(h.GitCatFile(dir, pr.Head.Sha, "project.build")))
|
||||||
// buildPrjBytes, err := h.GetPullRequestFileContent(pr, "project.build")
|
|
||||||
if h.HasError() {
|
if len(refPrj) < 1 {
|
||||||
h.LogPlainError(h.Error)
|
|
||||||
/*
|
|
||||||
_, err := h.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot find reference project")
|
_, err := h.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot find reference project")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.LogPlainError(err)
|
h.LogPlainError(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
*/
|
h.LogError("Cannot find reference project for %s PR#%d", pr.Base.Name, pr.Index)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if h.HasError() {
|
||||||
|
h.LogPlainError(h.Error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,33 +306,32 @@ func processPullNotification(h *common.RequestHandler, thread *models.Notificati
|
|||||||
// recreate missing project
|
// recreate missing project
|
||||||
h.LogError("missing OBS project ... recreating '%s': %v", obsProject, err)
|
h.LogError("missing OBS project ... recreating '%s': %v", obsProject, err)
|
||||||
startBuild(h, pr, obsClient)
|
startBuild(h, pr, obsClient)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.LogError("failed fetching build status for '%s': %v", obsProject, err)
|
h.LogError("failed fetching build status for '%s': %v", obsProject, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
refProjectResult, err := obsClient.BuildStatus(refProject)
|
refProjectResult, err := obsClient.BuildStatus(refPrj, prjResult.GetPackageList()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.LogError("failed fetching ref project status for '%s': %v", refProject, err)
|
h.LogError("failed fetching ref project status for '%s': %v", refPrj, err)
|
||||||
}
|
}
|
||||||
buildStatus := processBuildStatus(prjResult, refProjectResult)
|
buildStatus := processBuildStatus(h, prjResult, refProjectResult)
|
||||||
|
|
||||||
switch buildStatus {
|
switch buildStatus {
|
||||||
case BuildSuccess:
|
case BuildStatusSummarySuccess:
|
||||||
_, err := h.AddReviewComment(pr, common.ReviewStateApproved, "Build successful")
|
_, err := h.AddReviewComment(pr, common.ReviewStateApproved, "Build successful")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.LogPlainError(err)
|
h.LogPlainError(err)
|
||||||
}
|
}
|
||||||
case BuildFailed:
|
case BuildStatusSummaryFailed:
|
||||||
_, err := h.AddReviewComment(pr, common.ReviewStateRequestChanges, "Build failed")
|
_, err := h.AddReviewComment(pr, common.ReviewStateRequestChanges, "Build failed")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.LogPlainError(err)
|
h.LogPlainError(err)
|
||||||
}
|
}
|
||||||
case BuildBuilding:
|
|
||||||
}
|
}
|
||||||
|
// waiting for build results -- nothing to do
|
||||||
|
|
||||||
// waiting for build results
|
|
||||||
// project := getObsProjectAssociatedWithPr(obsClient.HomeProject, pr)
|
|
||||||
case common.ReviewStateApproved:
|
case common.ReviewStateApproved:
|
||||||
// done, mark notification as read
|
// done, mark notification as read
|
||||||
h.Log("processing request for success build ...")
|
h.Log("processing request for success build ...")
|
||||||
|
Loading…
Reference in New Issue
Block a user