From 9d2dbb1235aa16c6f72e9afcb023b4f3c712f2280ae06880662f879d6de3d954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Thu, 8 May 2025 10:57:17 +0200 Subject: [PATCH 01/18] Staging: include also changed directories Projects which are not using submodules as package directories still need to be handled in stagings. --- common/git_utils.go | 1 + 1 file changed, 1 insertion(+) diff --git a/common/git_utils.go b/common/git_utils.go index a7e76e2..720173d 100644 --- a/common/git_utils.go +++ b/common/git_utils.go @@ -904,6 +904,7 @@ func (e *GitHandlerImpl) GitSubmoduleList(gitPath, commitId string) (submoduleLi for _, te := range tree.items { if te.isTree() { trees[p+te.name+"/"] = te.hash + submoduleList[p+te.name] = te.hash } else if te.isSubmodule() { submoduleList[p+te.name] = te.hash } -- 2.51.1 From 77ef52f31746dbed4db277d3cb14935e057ec5a282d5a6e3dcd958e77bed2d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Mon, 1 Sep 2025 17:23:41 +0200 Subject: [PATCH 02/18] obs_staging: fix results compare Esp. with multiple repositories: * j counter was not reset * wrong limit of results used (from reference not stage project) * package name compare using == was always wrong (and unneeded here) --- obs-staging-bot/main.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index fd14c37..c589f34 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -225,7 +225,6 @@ func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status B slices.SortFunc(results, PackageBuildStatusSorter) slices.SortFunc(ref, PackageBuildStatusSorter) - j := 0 SomeSuccess = false for i := 0; i < len(results); i++ { res, ok := common.ObsBuildStatusDetails[results[i].Code] @@ -240,10 +239,11 @@ func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status B if !res.Success { // not failed if reference project also failed for same package here - for ; j < len(results) && strings.Compare(results[i].Package, ref[j].Package) < 0; j++ { + j := 0 + for ; j < len(ref) && strings.Compare(results[i].Package, ref[j].Package) != 0; j++ { } - if j < len(results) && results[i].Package == ref[j].Package { + if j < len(ref) && results[i].Package == ref[j].Package { refRes, ok := common.ObsBuildStatusDetails[ref[j].Code] if !ok { common.LogInfo("unknown ref package result code:", ref[j].Code, "package:", ref[j].Package) @@ -324,7 +324,6 @@ 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 = stagingPrj meta.Description = fmt.Sprintf(`Pull request build job PR#%d to branch %s of %s/%s`, -- 2.51.1 From d64e0512f979e1385b2a1a88f5c28dc2070db7327ee7e446a0cef23820743ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Tue, 23 Sep 2025 09:09:23 +0200 Subject: [PATCH 03/18] obs-staging: fix onlybuild for subdirectories The parameter only takes a package name, but not a full subdirectory string. So we hand over only the last part behind a / here --- obs-staging-bot/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index c589f34..28ba0d9 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -338,7 +338,10 @@ func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque urlPkg := make([]string, 0, len(modifiedOrNew)) for _, pkg := range modifiedOrNew { - urlPkg = append(urlPkg, "onlybuild="+url.QueryEscape(pkg)) + // FIXME: skip manifest subdirectories itself + // strip any leading directory name and just hand over last directory as package name + onlybuilds := strings.Split(pkg, "/") + urlPkg = append(urlPkg, "onlybuild="+url.QueryEscape(onlybuilds[len(onlybuilds)-1])) } meta.ScmSync = pr.Head.Repo.CloneURL + "?" + strings.Join(urlPkg, "&") + "#" + pr.Head.Sha if len(meta.ScmSync) >= 65535 { -- 2.51.1 From 8f260f7d93ae4572d402f5388128405f12f8b0fefc2b9ca9468bec0c45174822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Tue, 23 Sep 2025 16:55:06 +0200 Subject: [PATCH 04/18] obs-staging-bot: fix wrong acceptance Any pull request with multiple packages was always successful, just when one package was successful build. Dropping the "someSuccess" handling for now since it is currently broken and I got also complaints that we anyway never should accept broken submissions. Even when the failure did exist before. This would anyway break an mainteannce/patchinfo build. We may want to add a config option to allow the again under given circumstances. --- obs-staging-bot/main.go | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index 28ba0d9..6eb8303 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -171,23 +171,23 @@ func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatus slices.SortFunc(refProject.Result, BuildResultSorter) common.LogDebug("comparing results", len(project.Result), "vs. ref", len(refProject.Result)) - SomeSuccess := false for i := 0; i < len(project.Result); i++ { common.LogDebug("searching for", project.Result[i].Repository, "/", project.Result[i].Arch) j := 0 found: - for ; j < len(refProject.Result); j++ { + for j = 0; j < len(refProject.Result); j++ { if project.Result[i].Repository != refProject.Result[j].Repository || project.Result[i].Arch != refProject.Result[j].Arch { continue } common.LogDebug(" found match for @ idx:", j) - res, success := ProcessRepoBuildStatus(project.Result[i].Status, refProject.Result[j].Status) + res := ProcessRepoBuildStatus(project.Result[i].Status, refProject.Result[j].Status) switch res { case BuildStatusSummarySuccess: - SomeSuccess = SomeSuccess || success break found + case BuildStatusSummaryFailed: + return BuildStatusSummaryFailed default: return res } @@ -201,14 +201,10 @@ func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatus } } - if SomeSuccess { - return BuildStatusSummarySuccess - } - - return BuildStatusSummaryFailed + return BuildStatusSummarySuccess } -func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status BuildStatusSummary, SomeSuccess bool) { +func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status BuildStatusSummary) { PackageBuildStatusSorter := func(a, b *common.PackageBuildStatus) int { return strings.Compare(a.Package, b.Package) } @@ -225,16 +221,15 @@ func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status B slices.SortFunc(results, PackageBuildStatusSorter) slices.SortFunc(ref, PackageBuildStatusSorter) - SomeSuccess = false for i := 0; i < len(results); i++ { res, ok := common.ObsBuildStatusDetails[results[i].Code] if !ok { common.LogInfo("unknown package result code:", results[i].Code, "for package:", results[i].Package) - return BuildStatusSummaryUnknown, SomeSuccess + return BuildStatusSummaryUnknown } if !res.Finished { - return BuildStatusSummaryBuilding, SomeSuccess + return BuildStatusSummaryBuilding } if !res.Success { @@ -247,7 +242,7 @@ func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status B refRes, ok := common.ObsBuildStatusDetails[ref[j].Code] if !ok { common.LogInfo("unknown ref package result code:", ref[j].Code, "package:", ref[j].Package) - return BuildStatusSummaryUnknown, SomeSuccess + return BuildStatusSummaryUnknown } if !refRes.Finished { @@ -255,15 +250,13 @@ func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status B } if refRes.Success { - return BuildStatusSummaryFailed, SomeSuccess + return BuildStatusSummaryFailed } } - } else { - SomeSuccess = true } } - return BuildStatusSummarySuccess, SomeSuccess + return BuildStatusSummarySuccess } func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingPrj, buildPrj string, stagingMasterPrj string) (*common.ProjectMeta, error) { -- 2.51.1 From 4db25ed1a29db46865e02a272c09402a31fe831d9630c2fe6db71a71c855d17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Tue, 14 Oct 2025 16:08:20 +0200 Subject: [PATCH 05/18] Fix build status check when package source is in subdir We must not use the upper directory name as part of the package name. --- obs-staging-bot/main.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index 6eb8303..a53e0d2 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -26,6 +26,7 @@ import ( "net/url" "os" "path" + "path/filepath" "regexp" "runtime/debug" "slices" @@ -819,10 +820,13 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e if !stagingConfig.RebuildAll { for pkg, headOid := range headSubmodules { if baseOid, exists := baseSubmodules[pkg]; !exists || baseOid != headOid { + if pkg != "rpms" && pkg != "dependencies" { + _, spkg := filepath.Split(pkg) if exists { - modifiedPackages = append(modifiedPackages, pkg) + modifiedPackages = append(modifiedPackages, spkg) } else { - newPackages = append(newPackages, pkg) + newPackages = append(newPackages, spkg) + } } common.LogDebug(pkg, ":", baseOid, "->", headOid) } -- 2.51.1 From 3645fd6e1b59a2e37f2063b072e2adfa5230a87c812d21099014261e5f94513d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Fri, 17 Oct 2025 16:34:13 +0200 Subject: [PATCH 06/18] obs-staging-bot: fix wrong acceptance step 2 remove more code to ignore reference project entirely --- obs-staging-bot/main.go | 81 +++++------------------------------------ 1 file changed, 10 insertions(+), 71 deletions(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index a53e0d2..3912646 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -135,55 +135,15 @@ func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatus panic("Should not happen -- BuiltResultSorter equal repos?") } slices.SortFunc(project.Result, BuildResultSorter) - if refProject == nil { - // just return if buid finished and have some successes, since new package - common.LogInfo("New package. Only need some success...") - SomeSuccess := false - for i := 0; i < len(project.Result); i++ { - repoRes := project.Result[i] - repoResStatus, ok := common.ObsRepoStatusDetails[repoRes.Code] - if !ok { - common.LogDebug("cannot find code:", repoRes.Code) - return BuildStatusSummaryUnknown - } - if !repoResStatus.Finished { - return BuildStatusSummaryBuilding - } - - for _, pkg := range repoRes.Status { - pkgStatus, ok := common.ObsBuildStatusDetails[pkg.Code] - if !ok { - common.LogInfo("Unknown package build status:", pkg.Code, "for", pkg.Package) - common.LogDebug("Details:", pkg.Details) - } - - if pkgStatus.Success { - SomeSuccess = true - } - } - } - - if SomeSuccess { - return BuildStatusSummarySuccess - } - return BuildStatusSummaryFailed - } - - slices.SortFunc(refProject.Result, BuildResultSorter) common.LogDebug("comparing results", len(project.Result), "vs. ref", len(refProject.Result)) for i := 0; i < len(project.Result); i++ { common.LogDebug("searching for", project.Result[i].Repository, "/", project.Result[i].Arch) j := 0 found: - for j = 0; j < len(refProject.Result); j++ { - if project.Result[i].Repository != refProject.Result[j].Repository || - project.Result[i].Arch != refProject.Result[j].Arch { - continue - } - + for j = 0; j < len(project.Result); j++ { common.LogDebug(" found match for @ idx:", j) - res := ProcessRepoBuildStatus(project.Result[i].Status, refProject.Result[j].Status) + res := ProcessRepoBuildStatus(project.Result[i].Status) switch res { case BuildStatusSummarySuccess: break found @@ -205,22 +165,19 @@ func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatus return BuildStatusSummarySuccess } -func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status BuildStatusSummary) { - PackageBuildStatusSorter := func(a, b *common.PackageBuildStatus) int { - return strings.Compare(a.Package, b.Package) - } +func ProcessRepoBuildStatus(results []*common.PackageBuildStatus) (status BuildStatusSummary) { + + PackageBuildStatusSorter := func(a, b *common.PackageBuildStatus) int { + return strings.Compare(a.Package, b.Package) + } - common.LogDebug("******** REF: ") - data, _ := xml.MarshalIndent(ref, "", " ") - common.LogDebug(string(data)) common.LogDebug("******* RESULTS: ") - data, _ = xml.MarshalIndent(results, "", " ") + data, _ := xml.MarshalIndent(results, "", " ") common.LogDebug(string(data)) common.LogDebug("*******") // compare build result slices.SortFunc(results, PackageBuildStatusSorter) - slices.SortFunc(ref, PackageBuildStatusSorter) for i := 0; i < len(results); i++ { res, ok := common.ObsBuildStatusDetails[results[i].Code] @@ -234,26 +191,7 @@ func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status B } if !res.Success { - // not failed if reference project also failed for same package here - j := 0 - for ; j < len(ref) && strings.Compare(results[i].Package, ref[j].Package) != 0; j++ { - } - - if j < len(ref) && results[i].Package == ref[j].Package { - refRes, ok := common.ObsBuildStatusDetails[ref[j].Code] - if !ok { - common.LogInfo("unknown ref package result code:", ref[j].Code, "package:", ref[j].Package) - return BuildStatusSummaryUnknown - } - - if !refRes.Finished { - common.LogDebug("Not finished building in reference project?") - } - - if refRes.Success { - return BuildStatusSummaryFailed - } - } + return BuildStatusSummaryFailed } } @@ -316,6 +254,7 @@ func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque common.LogError("error fetching project meta for", buildPrj, ". Err:", err) return nil, err } + common.LogInfo("Meta: ", meta) // generate new project with paths pointinig back to original repos -- 2.51.1 From 50fe524467f25a6e97a1d715e25612fc137a4cd0d6b0d9e53b403009b05669c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Thu, 20 Nov 2025 16:38:15 +0100 Subject: [PATCH 07/18] obs-staging-bot: Get rid of reference project results entirely Currently all our release managers insist anyway of only green states --- obs-staging-bot/main.go | 37 ++++--------------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index 3912646..68275d5 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -110,33 +110,15 @@ const ( BuildStatusSummaryUnknown = 4 ) -func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary { - if _, finished := refProject.BuildResultSummary(); !finished { - common.LogDebug("refProject not finished building??") - return BuildStatusSummaryUnknown - } - +func ProcessBuildStatus(project *common.BuildResultList) BuildStatusSummary { 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` - BuildResultSorter := func(a, b *common.BuildResult) int { - if c := strings.Compare(a.Repository, b.Repository); c != 0 { - return c - } - if c := strings.Compare(a.Arch, b.Arch); c != 0 { - return c - } + //slices.SortFunc(project.Result, BuildResultSorter) - panic("Should not happen -- BuiltResultSorter equal repos?") - } - slices.SortFunc(project.Result, BuildResultSorter) - - common.LogDebug("comparing results", len(project.Result), "vs. ref", len(refProject.Result)) + common.LogDebug("build results", len(project.Result)) for i := 0; i < len(project.Result); i++ { common.LogDebug("searching for", project.Result[i].Repository, "/", project.Result[i].Arch) j := 0 @@ -153,13 +135,6 @@ func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatus return res } } - - if j >= len(refProject.Result) { - common.LogDebug("Cannot find results...") - common.LogDebug(project.Result[i]) - common.LogDebug(refProject.Result) - return BuildStatusSummaryUnknown - } } return BuildStatusSummarySuccess @@ -869,15 +844,11 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e gitea.AddComment(pr, msg) } - baseResult, err := ObsClient.LastBuildResults(stagingConfig.ObsProject, modifiedPackages...) - if err != nil { - common.LogError("failed fetching ref project status for", stagingConfig.ObsProject, ":", err) - } stagingResult, err := ObsClient.BuildStatus(stagingProject) if err != nil { common.LogError("failed fetching stage project status for", stagingProject, ":", err) } - buildStatus := ProcessBuildStatus(stagingResult, baseResult) + buildStatus := ProcessBuildStatus(stagingResult) done := false switch buildStatus { -- 2.51.1 From df3618a1245295766285d7c8fcefc1cce34983743ce837feb37987d3d2ba3527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Thu, 11 Sep 2025 10:07:20 +0200 Subject: [PATCH 08/18] workflow-pr: don't crash when no submodule got changed eg. config changes only --- workflow-pr/pr_processor.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workflow-pr/pr_processor.go b/workflow-pr/pr_processor.go index fe0b6cb..6bea75a 100644 --- a/workflow-pr/pr_processor.go +++ b/workflow-pr/pr_processor.go @@ -476,10 +476,10 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error { continue } } - } - if err = pr.UpdatePrjGitPR(prset); err != nil { - return err + if err = pr.UpdatePrjGitPR(prset); err != nil { + return err + } } } -- 2.51.1 From 9cf35e120302c50bfd9180bf7fe3b1eb77979f9fdc55dbc2e0bd92caa70dd49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Tue, 4 Nov 2025 17:10:47 +0100 Subject: [PATCH 09/18] HACK: disable close of pullrequests for now it hit too many ones --- workflow-pr/pr_processor.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/workflow-pr/pr_processor.go b/workflow-pr/pr_processor.go index 6bea75a..107e1b7 100644 --- a/workflow-pr/pr_processor.go +++ b/workflow-pr/pr_processor.go @@ -459,11 +459,11 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error { if _, ok := err.(*repository.RepoMergePullRequestConflict); !ok { common.PanicOnError(err) } - } else { - Gitea.AddComment(pr.PR, "Closing here because the associated Project PR has been closed.") - Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{ - State: "closed", - }) +// } else { +// Gitea.AddComment(pr.PR, "Closing here because the associated Project PR has been closed.") +// Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{ +// State: "closed", +// }) } } } -- 2.51.1 From 414ba2956be36546d0a2e49380aa5edf21698a025ba4a99e33fc4235017ad9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Thu, 20 Nov 2025 12:58:01 +0100 Subject: [PATCH 10/18] workflow-direct: use relative path when adding a submodule This solves the issue of using the right credentials based on the main repo. Also it allows to rename the organisation. --- workflow-direct/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workflow-direct/main.go b/workflow-direct/main.go index b6f1dfc..416f9ec 100644 --- a/workflow-direct/main.go +++ b/workflow-direct/main.go @@ -123,7 +123,7 @@ func processConfiguredRepositoryAction(action *common.RepositoryWebhookEvent, co common.LogError(" - ", action.Repository.Name, "repo is not sha256. Ignoring.") return } - common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name)) + common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", "../" + action.Repository.Name, action.Repository.Name)) defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f") branch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, action.Repository.Name), "branch", "--show-current")) @@ -215,7 +215,7 @@ func processConfiguredPushAction(action *common.PushWebhookEvent, config *common } if stat, err := os.Stat(filepath.Join(git.GetPath(), gitPrj, action.Repository.Name)); err != nil { - git.GitExecOrPanic(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name) + git.GitExecOrPanic(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", "../" + action.Repository.Name, action.Repository.Name) common.LogDebug("Pushed to package that is not part of the project. Re-adding...", err) } else if !stat.IsDir() { common.LogError("Pushed to a package that is not a submodule but exists in the project. Ignoring.") @@ -420,7 +420,7 @@ next_repo: } // add repository to git project - common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", r.CloneURL, r.Name)) + common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", "../" + r.Name, r.Name)) curBranch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, r.Name), "branch", "--show-current")) if branch != curBranch { -- 2.51.1 From b315ee22ccfd066421606b7618c1a7d4ea7f8a46cd2e6050740cc8ea7da11d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Wed, 26 Nov 2025 09:16:51 +0100 Subject: [PATCH 11/18] Support remote source in pull requests This requires write permission by maintainer there --- common/config.go | 6 +++--- common/git_utils.go | 4 ++-- common/obs_utils.go | 12 +++++++----- workflow-pr/README.md | 1 + workflow-pr/pr_processor.go | 15 +++++++++------ 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/common/config.go b/common/config.go index dceccf2..7a65355 100644 --- a/common/config.go +++ b/common/config.go @@ -62,9 +62,9 @@ type Permissions struct { } const ( - Label_StagingAuto = "staging/Auto" - Label_ReviewPending = "review/Pending" - Label_ReviewDone = "review/Done" + Label_StagingAuto = "staging/Auto" + Label_ReviewPending = "review/Pending" + Label_ReviewDone = "review/Done" ) func LabelKey(tag_value string) string { diff --git a/common/git_utils.go b/common/git_utils.go index 720173d..761e79e 100644 --- a/common/git_utils.go +++ b/common/git_utils.go @@ -283,7 +283,7 @@ func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error func (e *GitHandlerImpl) GitBranchHead(gitDir, branchName string) (string, error) { id, err := e.GitExecWithOutput(gitDir, "show-ref", "--heads", "--hash", branchName) if err != nil { - return "", fmt.Errorf("Can't find default branch: %s", branchName) + return "", fmt.Errorf("Can't find default branch: %s in %s", branchName, gitDir) } id = strings.TrimSpace(SplitLines(id)[0]) @@ -297,7 +297,7 @@ func (e *GitHandlerImpl) GitBranchHead(gitDir, branchName string) (string, error func (e *GitHandlerImpl) GitRemoteHead(gitDir, remote, branchName string) (string, error) { id, err := e.GitExecWithOutput(gitDir, "show-ref", "--hash", "--verify", "refs/remotes/"+remote+"/"+branchName) if err != nil { - return "", fmt.Errorf("Can't find default branch: %s", branchName) + return "", fmt.Errorf("Can't find default branch: %s in %s", branchName, gitDir) } return strings.TrimSpace(id), nil diff --git a/common/obs_utils.go b/common/obs_utils.go index 817367e..ac0766a 100644 --- a/common/obs_utils.go +++ b/common/obs_utils.go @@ -689,13 +689,15 @@ func (r *BuildResultList) BuildResultSummary() (success, finished bool) { if !ok { panic("Unknown result code: " + result.Code) } - if r.isLastBuild && result.Code == "unknown" { - // it means the package has never build yet, - // but we don't know the reason - detail.Finished = true + if r.isLastBuild { + // we are always finished, since it is the last result + // also when there is "unknown" state, it just means it + // it was never done + finished = true + } else { + finished = finished && detail.Finished } - finished = finished && detail.Finished success = success && detail.Success if !finished { diff --git a/workflow-pr/README.md b/workflow-pr/README.md index 0612c2b..875f74a 100644 --- a/workflow-pr/README.md +++ b/workflow-pr/README.md @@ -85,6 +85,7 @@ The following labels are used, when defined in Repo/Org. | StagingAuto | staging/Auto | Assigned to Project Git PRs when first staged | ReviewPending | review/Pending | Assigned to PR when reviews are still pending | ReviewDone | review/Done | Assigned to PR when reviews are complete on this particular PR +| ProjectUpdated | | Assigned if defined and git project got modified Maintainership diff --git a/workflow-pr/pr_processor.go b/workflow-pr/pr_processor.go index 107e1b7..cadeec3 100644 --- a/workflow-pr/pr_processor.go +++ b/workflow-pr/pr_processor.go @@ -297,14 +297,16 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error { PrjGit := PrjGitPR.PR.Base.Repo prjGitPRbranch := PrjGitPR.PR.Head.Name if PrjGitPR.PR.Base.RepoID != PrjGitPR.PR.Head.RepoID { - PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, "", PrjGit.SSHURL) - git.GitExecOrPanic(common.DefaultGitPrj, "fetch", PrjGitPR.RemoteName, PrjGitPR.PR.Head.Sha) - git.GitExecOrPanic(common.DefaultGitPrj, "checkout", PrjGitPR.PR.Head.Sha) - common.LogInfo("Cannot update this PR as it's on another remote, not branch:", prjGitPRbranch, "Assuming this is by-design. (eg. project git PR only)") - return nil + // permission check, if submission comes from foreign repo + if !PrjGitPR.PR.AllowMaintainerEdit { + common.LogError("Warning: source and target branch are in different repositories. We may not have the right permissions...") + Gitea.AddComment(PrjGitPR.PR, "This PR does not allow maintainer changes, but referenced package branch has changed!") + return nil + } } - PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, prjGitPRbranch, PrjGit.SSHURL) +// PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, prjGitPRbranch, PrjGit.SSHURL) + PrjGitPR.RemoteName, err = git.GitClone(common.DefaultGitPrj, PrjGitPR.PR.Head.Ref, PrjGitPR.PR.Head.Repo.SSHURL) common.PanicOnError(err) git.GitExecOrPanic(common.DefaultGitPrj, "fetch", PrjGitPR.RemoteName, PrjGitBranch) @@ -352,6 +354,7 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error { } common.PanicOnError(git.GitExec(common.DefaultGitPrj, params...)) PrjGitPR.PR.Head.Sha = newHeadCommit + Gitea.SetLabels(PrjGit.Owner.UserName, PrjGit.Name, PrjGitPR.PR.Index, []string{prset.Config.Label("PR/updated")}) } // update PR -- 2.51.1 From 7a3e1f66112aa056948a6d78c9fd1014931cb7ef624e4c9dd6eded9e2ef6440b Mon Sep 17 00:00:00 2001 From: Adam Majer Date: Fri, 28 Nov 2025 12:25:58 +0100 Subject: [PATCH 12/18] pr: only update PR if elided title not changed Gitea trims long titles so we need to compare if the trimmed length is same, not entire string that will always differ. --- workflow-pr/pr_processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow-pr/pr_processor.go b/workflow-pr/pr_processor.go index cadeec3..bbf9ee1 100644 --- a/workflow-pr/pr_processor.go +++ b/workflow-pr/pr_processor.go @@ -371,7 +371,7 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error { } return CurrentTitle == NewTitle } - if PrjGitPR.PR.User.UserName == CurrentUser.UserName && (PrjGitPR.PR.Body != PrjGitBody || !isPrTitleSame(PrjGitPR.PR.Title, PrjGitTitle)) { + if !pr.config.NoProjectGitPR && PrjGitPR.PR.User.UserName == CurrentUser.UserName && (PrjGitPR.PR.Body != PrjGitBody || !isPrTitleSame(PrjGitPR.PR.Title, PrjGitTitle)) { Gitea.UpdatePullRequest(PrjGit.Owner.UserName, PrjGit.Name, PrjGitPR.PR.Index, &models.EditPullRequestOption{ RemoveDeadline: true, Title: PrjGitTitle, -- 2.51.1 From 72ec17d7833b7c3a7aca4d7a177a90b4e8e6507b45bf05b8d47d6ee0d79e4051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Fri, 28 Nov 2025 13:34:01 +0100 Subject: [PATCH 13/18] Disable temporary comment adding in case of lacking permissions --- workflow-pr/pr_processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow-pr/pr_processor.go b/workflow-pr/pr_processor.go index bbf9ee1..b7b7260 100644 --- a/workflow-pr/pr_processor.go +++ b/workflow-pr/pr_processor.go @@ -300,7 +300,7 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error { // permission check, if submission comes from foreign repo if !PrjGitPR.PR.AllowMaintainerEdit { common.LogError("Warning: source and target branch are in different repositories. We may not have the right permissions...") - Gitea.AddComment(PrjGitPR.PR, "This PR does not allow maintainer changes, but referenced package branch has changed!") + // Gitea.AddComment(PrjGitPR.PR, "This PR does not allow maintainer changes, but referenced package branch has changed!") return nil } } -- 2.51.1 From d024c1f786958375b0c99151d4564cc7bc5ce7b9fa9994f4881e27b2654fd004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Fri, 28 Nov 2025 14:49:22 +0100 Subject: [PATCH 14/18] Transfer WIP: from package pool requests to project --- workflow-pr/pr_processor.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/workflow-pr/pr_processor.go b/workflow-pr/pr_processor.go index b7b7260..7cc021b 100644 --- a/workflow-pr/pr_processor.go +++ b/workflow-pr/pr_processor.go @@ -26,6 +26,7 @@ func PrjGitDescription(prset *common.PRSet) (title string, desc string) { title_refs := make([]string, 0, len(prset.PRs)-1) refs := make([]string, 0, len(prset.PRs)-1) + prefix := "" for _, pr := range prset.PRs { if prset.IsPrjGitPR(pr.PR) { continue @@ -34,6 +35,9 @@ func PrjGitDescription(prset *common.PRSet) (title string, desc string) { // remove PRs that are not open from description continue } + if strings.HasPrefix(pr.PR.Title, "WIP:") { + prefix = "WIP: " + } org, repo, idx := pr.PRComponents() title_refs = append(title_refs, repo) @@ -44,7 +48,7 @@ func PrjGitDescription(prset *common.PRSet) (title string, desc string) { slices.Sort(title_refs) slices.Sort(refs) - title = "Forwarded PRs: " + strings.Join(title_refs, ", ") + title = prefix + "Forwarded PRs: " + strings.Join(title_refs, ", ") desc = fmt.Sprintf("This is a forwarded pull request by %s\nreferencing the following pull request(s):\n\n", GitAuthor) + strings.Join(refs, "\n") + "\n" if prset.Config.ManualMergeOnly { -- 2.51.1 From 6ffb596aba9879ccdd090f568bac8cb17c9cf45caebb3160b11e1464721c57b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Tue, 13 Jan 2026 14:00:28 +0100 Subject: [PATCH 15/18] Add an "lfs fsck" check after submodule update to avoid merging pull requests where lfs objects are not correctly registered. Can happen when user has not installed lfs for example. --- workflow-pr/pr_processor.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/workflow-pr/pr_processor.go b/workflow-pr/pr_processor.go index 7cc021b..5fb7de3 100644 --- a/workflow-pr/pr_processor.go +++ b/workflow-pr/pr_processor.go @@ -182,6 +182,33 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error { } updateSubmoduleInPR(submodulePath, prHead, git) + err := git.GitExec(path.Join(common.DefaultGitPrj, submodulePath), "lfs", "fetch") + common.LogError("lfs fetch err: ", err) + if err = git.GitExec(path.Join(common.DefaultGitPrj, submodulePath), "lfs", "fsck"); err != nil { + + found_comment := false + timeline, terr := common.FetchTimelineSinceLastPush(Gitea, prHead, org, repo, idx) + if terr != nil { + common.LogError("lfs fsck error, but timeline fetch failed") + break + } + msgPrefix := "The LFS objects are broken!" + for _, t := range timeline { + if t.Type == common.TimelineCommentType_Comment && strings.HasPrefix(t.Body, msgPrefix) { + found_comment = true + common.LogError("lfs fsck Comment already found") + + break + } + } + + if !found_comment && !common.IsDryRun { + Gitea.AddComment(pr.PR, msgPrefix + " Please verify with 'git lfs fsck'") + } + common.LogError("lfs fsck failed with: ", err.Error()) + return err + } + status, err := git.GitStatus(common.DefaultGitPrj) common.LogDebug("status:", status) common.LogDebug("submodule", repo, " hash:", id, " -> ", prHead) -- 2.51.1 From 442b3d7c0f3736fd5fc88f1ce0dbf0f9cb73a29ecc464bd95e801504e81cd60f Mon Sep 17 00:00:00 2001 From: Antonello Tartamo Date: Tue, 27 Jan 2026 16:50:40 +0100 Subject: [PATCH 16/18] Forward build status to package PR/s --- obs-staging-bot/main.go | 89 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index 68275d5..9c494c5 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -19,6 +19,7 @@ package main */ import ( + "bufio" "encoding/xml" "errors" "flag" @@ -173,6 +174,35 @@ func ProcessRepoBuildStatus(results []*common.PackageBuildStatus) (status BuildS return BuildStatusSummarySuccess } +func GetPackageBuildStatus(project *common.BuildResultList, packageName string) (bool, BuildStatusSummary) { + err := true + + for _, result := range project.Result { + for _, pkgStatus := range result.Status { + if pkgStatus.Package == packageName { + err = false + + res, ok := common.ObsBuildStatusDetails[pkgStatus.Code] + if !ok { + common.LogInfo("unknown package result code:", pkgStatus.Code, "for package:", pkgStatus.Package) + return err, BuildStatusSummaryUnknown + } + + if !res.Finished { + return err, BuildStatusSummaryBuilding + } + + if !res.Success { + return err, BuildStatusSummaryFailed + } + return err, BuildStatusSummarySuccess + } + } + } + + return err, BuildStatusSummaryUnknown +} + func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingPrj, buildPrj string, stagingMasterPrj string) (*common.ProjectMeta, error) { common.LogDebug("repo content fetching ...") err := FetchPrGit(git, pr) @@ -593,6 +623,24 @@ func SetStatus(gitea common.Gitea, org, repo, hash string, status *models.Commit return err } +func commentOnPackagePR(gitea common.Gitea, org string, repo string, prNum int64, msg string) { + if IsDryRun { + common.LogInfo("Would comment on package PR %s/%s#%d: %s", org, repo, prNum, msg) + return + } + + pr, err := gitea.GetPullRequest(org, repo, prNum) + if err != nil { + common.LogError("Failed to get package PR %s/%s#%d: %v", org, repo, prNum, err) + return + } + + err = gitea.AddComment(pr, msg) + if err != nil { + common.LogError("Failed to comment on package PR %s/%s#%d: %v", org, repo, prNum, err) + } +} + func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, error) { dir, err := os.MkdirTemp(os.TempDir(), BotName) common.PanicOnError(err) @@ -848,10 +896,12 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e if err != nil { common.LogError("failed fetching stage project status for", stagingProject, ":", err) } - buildStatus := ProcessBuildStatus(stagingResult) + + _, packagePRs := common.ExtractDescriptionAndPRs(bufio.NewScanner(strings.NewReader(pr.Body))) done := false - switch buildStatus { + overallBuildStatus := ProcessBuildStatus(stagingResult) + switch overallBuildStatus { case BuildStatusSummarySuccess: status.Status = common.CommitStatus_Success done = true @@ -871,7 +921,40 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e } } } - common.LogInfo("Build status:", buildStatus) + + missingPkgs := []string{} + + for _, packagePR := range packagePRs { + missing, packageBuildStatus := GetPackageBuildStatus(stagingResult, packagePR.Repo) + if missing { + missingPkgs = append(missingPkgs, packagePR.Repo) + continue + } + var msg string + switch packageBuildStatus { + case BuildStatusSummarySuccess: + msg = "Build successful" + case BuildStatusSummaryFailed: + msg = "Build failed" + default: + continue + } + commentOnPackagePR(gitea, packagePR.Org, packagePR.Repo, packagePR.Num, msg) + } + + if len(missingPkgs) > 0 { + msg := "The following packages were not found in the staging project:\n" + for _, pkg := range missingPkgs { + msg = msg + " - " + pkg + "\n" + } + common.LogInfo(msg) + err := gitea.AddComment(pr, msg) + if err != nil { + common.LogError(err) + } + } + + common.LogInfo("Build status:", overallBuildStatus) if !IsDryRun { if err = SetStatus(gitea, org, repo, pr.Head.Sha, status); err != nil { return false, err -- 2.51.1 From 7473609e8390dbf3a10be781e622c403cf99cf1c58b228d5c2317c24fe5df4c1 Mon Sep 17 00:00:00 2001 From: Antonello Tartamo Date: Wed, 28 Jan 2026 13:07:34 +0100 Subject: [PATCH 17/18] fixed spamming comments on package PRs when build is in progress --- obs-staging-bot/main.go | 57 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index 9c494c5..09a6051 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -474,7 +474,7 @@ func FetchOurLatestActionableReview(gitea common.Gitea, org, repo string, id int } func ParseNotificationToPR(thread *models.NotificationThread) (org string, repo string, num int64, err error) { - rx := regexp.MustCompile(`^https://src\.(?:open)?suse\.(?:org|de)/api/v\d+/repos/(?[-_a-zA-Z0-9]+)/(?[-_a-zA-Z0-9]+)/issues/(?[0-9]+)$`) + rx := regexp.MustCompile(`^.*/api/v\d+/repos/(?[-_a-zA-Z0-9]+)/(?[-_a-zA-Z0-9]+)/issues/(?[0-9]+)$`) notification := thread.Subject match := rx.FindStringSubmatch(notification.URL) if match == nil { @@ -922,35 +922,38 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e } } - missingPkgs := []string{} + if overallBuildStatus == BuildStatusSummarySuccess || overallBuildStatus == BuildStatusSummaryFailed { + // avoid commenting while build is in progress + missingPkgs := []string{} - for _, packagePR := range packagePRs { - missing, packageBuildStatus := GetPackageBuildStatus(stagingResult, packagePR.Repo) - if missing { - missingPkgs = append(missingPkgs, packagePR.Repo) - continue + for _, packagePR := range packagePRs { + missing, packageBuildStatus := GetPackageBuildStatus(stagingResult, packagePR.Repo) + if missing { + missingPkgs = append(missingPkgs, packagePR.Repo) + continue + } + var msg string + switch packageBuildStatus { + case BuildStatusSummarySuccess: + msg = "Build successful" + case BuildStatusSummaryFailed: + msg = "Build failed" + default: + continue + } + commentOnPackagePR(gitea, packagePR.Org, packagePR.Repo, packagePR.Num, msg) } - var msg string - switch packageBuildStatus { - case BuildStatusSummarySuccess: - msg = "Build successful" - case BuildStatusSummaryFailed: - msg = "Build failed" - default: - continue - } - commentOnPackagePR(gitea, packagePR.Org, packagePR.Repo, packagePR.Num, msg) - } - if len(missingPkgs) > 0 { - msg := "The following packages were not found in the staging project:\n" - for _, pkg := range missingPkgs { - msg = msg + " - " + pkg + "\n" - } - common.LogInfo(msg) - err := gitea.AddComment(pr, msg) - if err != nil { - common.LogError(err) + if len(missingPkgs) > 0 { + msg := "The following packages were not found in the staging project:\n" + for _, pkg := range missingPkgs { + msg = msg + " - " + pkg + "\n" + } + common.LogInfo(msg) + err := gitea.AddComment(pr, msg) + if err != nil { + common.LogError(err) + } } } -- 2.51.1 From ee288124ccaca2146b5d54d88b83e0310581f1c9cb7c9d0a930499fe8473a33e Mon Sep 17 00:00:00 2001 From: Antonello Tartamo Date: Wed, 28 Jan 2026 14:19:46 +0100 Subject: [PATCH 18/18] fixed check for multiple repos and architectures, added a link to the OBS project --- obs-staging-bot/main.go | 52 +++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/obs-staging-bot/main.go b/obs-staging-bot/main.go index 09a6051..7350aaa 100644 --- a/obs-staging-bot/main.go +++ b/obs-staging-bot/main.go @@ -175,32 +175,44 @@ func ProcessRepoBuildStatus(results []*common.PackageBuildStatus) (status BuildS } func GetPackageBuildStatus(project *common.BuildResultList, packageName string) (bool, BuildStatusSummary) { - err := true + var packageStatuses []*common.PackageBuildStatus + // Collect all statuses for the package for _, result := range project.Result { for _, pkgStatus := range result.Status { if pkgStatus.Package == packageName { - err = false - - res, ok := common.ObsBuildStatusDetails[pkgStatus.Code] - if !ok { - common.LogInfo("unknown package result code:", pkgStatus.Code, "for package:", pkgStatus.Package) - return err, BuildStatusSummaryUnknown - } - - if !res.Finished { - return err, BuildStatusSummaryBuilding - } - - if !res.Success { - return err, BuildStatusSummaryFailed - } - return err, BuildStatusSummarySuccess + packageStatuses = append(packageStatuses, pkgStatus) } } } - return err, BuildStatusSummaryUnknown + if len(packageStatuses) == 0 { + return true, BuildStatusSummaryUnknown // true for 'missing' + } + + // Check for any failures + for _, pkgStatus := range packageStatuses { + res, ok := common.ObsBuildStatusDetails[pkgStatus.Code] + if !ok { + common.LogInfo("unknown package result code:", pkgStatus.Code, "for package:", pkgStatus.Package) + return false, BuildStatusSummaryUnknown + } + if !res.Success { + return false, BuildStatusSummaryFailed + } + } + + // Check for any unfinished builds + for _, pkgStatus := range packageStatuses { + res, _ := common.ObsBuildStatusDetails[pkgStatus.Code] + // 'ok' is already checked in the loop above + if !res.Finished { + return false, BuildStatusSummaryBuilding + } + } + + // If we got here, all are finished and successful + return false, BuildStatusSummarySuccess } func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingPrj, buildPrj string, stagingMasterPrj string) (*common.ProjectMeta, error) { @@ -935,9 +947,9 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e var msg string switch packageBuildStatus { case BuildStatusSummarySuccess: - msg = "Build successful" + msg = fmt.Sprintf("Build successful, for more information go in %s/project/show/%s.\n", ObsWebHost, stagingProject) case BuildStatusSummaryFailed: - msg = "Build failed" + msg = fmt.Sprintf("Build failed, for more information go in %s/project/show/%s.\n", ObsWebHost, stagingProject) default: continue } -- 2.51.1