diff --git a/bots-common/obs_utils.go b/bots-common/obs_utils.go index b500ab7..64b8857 100644 --- a/bots-common/obs_utils.go +++ b/bots-common/obs_utils.go @@ -55,6 +55,7 @@ type ProjectMeta struct { Name string `xml:"name,attr"` Title string `xml:"title"` Description string `xml:"description"` + Url string `xml:"url"` ScmSync string `xml:"scmsync"` Repositories []RepositoryMeta `xml:"repository"` @@ -116,18 +117,18 @@ func ObsSafeProjectName(prjname string) string { switch prjname[0] { case '_', '.', ':': prjname = "X" + prjname[1:] -// no UTF-8 in OBS :( -// prjname = "_" + prjname[1:] -// case ':': -// prjname = ":" + prjname[1:] -// case '.': -// prjname = "․" + prjname[1:] + // no UTF-8 in OBS :( + // prjname = "_" + prjname[1:] + // case ':': + // prjname = ":" + prjname[1:] + // case '.': + // prjname = "․" + prjname[1:] } return prjname } -func (c *ObsClient) SetProjectMeta(meta *ProjectMeta) (error) { +func (c *ObsClient) SetProjectMeta(meta *ProjectMeta) error { req, err := http.NewRequest("PUT", c.baseUrl.JoinPath("source", meta.Name, "_meta").String(), nil) if err != nil { @@ -219,6 +220,14 @@ func parseBuildResults(data []byte) (*BuildResultList, error) { return &result, nil } +type ObsProjectNotFound struct { + Project string +} + +func (obs ObsProjectNotFound) Error() string { + return fmt.Sprintf("OBS project is not found: %s", obs.Project) +} + func (c *ObsClient) BuildStatus(project string, packages ...string) (*BuildResultList, error) { u := c.baseUrl.JoinPath("build", project, "_result") query := u.Query() @@ -242,7 +251,12 @@ func (c *ObsClient) BuildStatus(project string, packages ...string) (*BuildResul return nil, err } - if res.StatusCode != 200 { + switch res.StatusCode { + case 200: + break + case 404: + return nil, ObsProjectNotFound{project} + default: return nil, fmt.Errorf("Unexpected return code: %d", res.StatusCode) } diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index 48972a6..4fc7e32 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -1,6 +1,8 @@ package main import ( + "bytes" + "errors" "fmt" "log" "net/url" @@ -65,6 +67,16 @@ 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 @@ -99,20 +111,21 @@ func startBuild(h *common.RequestHandler, pr *models.PullRequest, obsClient *com } h.Log("repo content fetching ...") - buildPrjBytes := h.GitCatFile(dir, pr.Head.Sha, "project.build") - // buildPrjBytes, err := h.GetPullRequestFileContent(pr, "project.build") + buildPrj := string(bytes.TrimSpace(h.GitCatFile(dir, pr.Head.Sha, "project.build"))) + + if len(buildPrj) < 1 { + _, err := h.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot find reference project") + if err != nil { + h.LogPlainError(err) + return h.Error + } + return fmt.Errorf("Cannot find reference project for %s PR#%d", pr.Base.Name, pr.Index) + } if h.HasError() { h.LogPlainError(h.Error) - /* - _, err := h.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot find reference project") - if err != nil { - h.LogPlainError(err) - } - */ return h.Error } - buildPrj := strings.TrimSpace(string(buildPrjBytes)) meta, err := obsClient.GetProjectMeta(buildPrj) if err != nil { h.Log("error fetching project meta for %s: %v", buildPrj, err) @@ -122,15 +135,21 @@ func startBuild(h *common.RequestHandler, pr *models.PullRequest, obsClient *com // generate new project with paths pointinig back to original repos // disable publishing - // TODO: escape things here meta.Name = getObsProjectAssociatedWithPr(obsClient.HomeProject, pr) - meta.Description = fmt.Sprintf(`Pull request build job: %s%s PR#%d`, - "https://src.opensuse.org", pr.Base.Repo.Name, pr.Index) + 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( + "https://src.opensuse.org/%s/%s/pulls/%d", + url.PathEscape(pr.Base.Repo.Owner.UserName), + url.PathEscape(pr.Base.Repo.Name), + pr.Index, + ) + urlPkg := make([]string, 0, len(modifiedOrNew)) for _, pkg := range modifiedOrNew { urlPkg = append(urlPkg, "onlybuild="+url.QueryEscape(pkg)) } - meta.ScmSync = pr.Head.Repo.CloneURL + "?" + strings.Join(urlPkg, "&") + meta.ScmSync = pr.Head.Repo.CloneURL + "?" + strings.Join(urlPkg, "&") + "#" + pr.Head.Sha meta.Title = fmt.Sprintf("PR#%d to %s", pr.Index, pr.Base.Name) meta.PublicFlags = common.Flags{Contents: ""} @@ -264,6 +283,12 @@ func processPullNotification(h *common.RequestHandler, thread *models.Notificati obsProject := getObsProjectAssociatedWithPr(obsClient.HomeProject, pr) prjResult, err := obsClient.BuildStatus(obsProject) if err != nil { + if errors.Is(err, common.ObsProjectNotFound{Project: obsProject}) { + // recreate missing project + h.LogError("missing OBS project ... recreating '%s': %v", obsProject, err) + startBuild(h, pr, obsClient) + return + } h.LogError("failed fetching build status for '%s': %v", obsProject, err) return }