staging-updates #136

Manually merged
adamm merged 17 commits from staging-updates into main 2026-02-25 14:49:39 +01:00
4 changed files with 1941 additions and 75 deletions

View File

@@ -83,3 +83,260 @@ func (c *MockObsStatusFetcherWithStateBuildStatusWithStateCall) DoAndReturn(f fu
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockObsClientInterface is a mock of ObsClientInterface interface.
type MockObsClientInterface struct {
ctrl *gomock.Controller
recorder *MockObsClientInterfaceMockRecorder
isgomock struct{}
}
// MockObsClientInterfaceMockRecorder is the mock recorder for MockObsClientInterface.
type MockObsClientInterfaceMockRecorder struct {
mock *MockObsClientInterface
}
// NewMockObsClientInterface creates a new mock instance.
func NewMockObsClientInterface(ctrl *gomock.Controller) *MockObsClientInterface {
mock := &MockObsClientInterface{ctrl: ctrl}
mock.recorder = &MockObsClientInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockObsClientInterface) EXPECT() *MockObsClientInterfaceMockRecorder {
return m.recorder
}
// BuildStatus mocks base method.
func (m *MockObsClientInterface) BuildStatus(project string, packages ...string) (*common.BuildResultList, error) {
m.ctrl.T.Helper()
varargs := []any{project}
for _, a := range packages {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "BuildStatus", varargs...)
ret0, _ := ret[0].(*common.BuildResultList)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BuildStatus indicates an expected call of BuildStatus.
func (mr *MockObsClientInterfaceMockRecorder) BuildStatus(project any, packages ...any) *MockObsClientInterfaceBuildStatusCall {
mr.mock.ctrl.T.Helper()
varargs := append([]any{project}, packages...)
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildStatus", reflect.TypeOf((*MockObsClientInterface)(nil).BuildStatus), varargs...)
return &MockObsClientInterfaceBuildStatusCall{Call: call}
}
// MockObsClientInterfaceBuildStatusCall wrap *gomock.Call
type MockObsClientInterfaceBuildStatusCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockObsClientInterfaceBuildStatusCall) Return(arg0 *common.BuildResultList, arg1 error) *MockObsClientInterfaceBuildStatusCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockObsClientInterfaceBuildStatusCall) Do(f func(string, ...string) (*common.BuildResultList, error)) *MockObsClientInterfaceBuildStatusCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockObsClientInterfaceBuildStatusCall) DoAndReturn(f func(string, ...string) (*common.BuildResultList, error)) *MockObsClientInterfaceBuildStatusCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// DeleteProject mocks base method.
func (m *MockObsClientInterface) DeleteProject(project string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteProject", project)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteProject indicates an expected call of DeleteProject.
func (mr *MockObsClientInterfaceMockRecorder) DeleteProject(project any) *MockObsClientInterfaceDeleteProjectCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteProject", reflect.TypeOf((*MockObsClientInterface)(nil).DeleteProject), project)
return &MockObsClientInterfaceDeleteProjectCall{Call: call}
}
// MockObsClientInterfaceDeleteProjectCall wrap *gomock.Call
type MockObsClientInterfaceDeleteProjectCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockObsClientInterfaceDeleteProjectCall) Return(arg0 error) *MockObsClientInterfaceDeleteProjectCall {
c.Call = c.Call.Return(arg0)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockObsClientInterfaceDeleteProjectCall) Do(f func(string) error) *MockObsClientInterfaceDeleteProjectCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockObsClientInterfaceDeleteProjectCall) DoAndReturn(f func(string) error) *MockObsClientInterfaceDeleteProjectCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// GetHomeProject mocks base method.
func (m *MockObsClientInterface) GetHomeProject() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetHomeProject")
ret0, _ := ret[0].(string)
return ret0
}
// GetHomeProject indicates an expected call of GetHomeProject.
func (mr *MockObsClientInterfaceMockRecorder) GetHomeProject() *MockObsClientInterfaceGetHomeProjectCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHomeProject", reflect.TypeOf((*MockObsClientInterface)(nil).GetHomeProject))
return &MockObsClientInterfaceGetHomeProjectCall{Call: call}
}
// MockObsClientInterfaceGetHomeProjectCall wrap *gomock.Call
type MockObsClientInterfaceGetHomeProjectCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockObsClientInterfaceGetHomeProjectCall) Return(arg0 string) *MockObsClientInterfaceGetHomeProjectCall {
c.Call = c.Call.Return(arg0)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockObsClientInterfaceGetHomeProjectCall) Do(f func() string) *MockObsClientInterfaceGetHomeProjectCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockObsClientInterfaceGetHomeProjectCall) DoAndReturn(f func() string) *MockObsClientInterfaceGetHomeProjectCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// GetProjectMeta mocks base method.
func (m *MockObsClientInterface) GetProjectMeta(project string) (*common.ProjectMeta, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetProjectMeta", project)
ret0, _ := ret[0].(*common.ProjectMeta)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetProjectMeta indicates an expected call of GetProjectMeta.
func (mr *MockObsClientInterfaceMockRecorder) GetProjectMeta(project any) *MockObsClientInterfaceGetProjectMetaCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProjectMeta", reflect.TypeOf((*MockObsClientInterface)(nil).GetProjectMeta), project)
return &MockObsClientInterfaceGetProjectMetaCall{Call: call}
}
// MockObsClientInterfaceGetProjectMetaCall wrap *gomock.Call
type MockObsClientInterfaceGetProjectMetaCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockObsClientInterfaceGetProjectMetaCall) Return(arg0 *common.ProjectMeta, arg1 error) *MockObsClientInterfaceGetProjectMetaCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockObsClientInterfaceGetProjectMetaCall) Do(f func(string) (*common.ProjectMeta, error)) *MockObsClientInterfaceGetProjectMetaCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockObsClientInterfaceGetProjectMetaCall) DoAndReturn(f func(string) (*common.ProjectMeta, error)) *MockObsClientInterfaceGetProjectMetaCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// SetHomeProject mocks base method.
func (m *MockObsClientInterface) SetHomeProject(project string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetHomeProject", project)
}
// SetHomeProject indicates an expected call of SetHomeProject.
func (mr *MockObsClientInterfaceMockRecorder) SetHomeProject(project any) *MockObsClientInterfaceSetHomeProjectCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHomeProject", reflect.TypeOf((*MockObsClientInterface)(nil).SetHomeProject), project)
return &MockObsClientInterfaceSetHomeProjectCall{Call: call}
}
// MockObsClientInterfaceSetHomeProjectCall wrap *gomock.Call
type MockObsClientInterfaceSetHomeProjectCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockObsClientInterfaceSetHomeProjectCall) Return() *MockObsClientInterfaceSetHomeProjectCall {
c.Call = c.Call.Return()
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockObsClientInterfaceSetHomeProjectCall) Do(f func(string)) *MockObsClientInterfaceSetHomeProjectCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockObsClientInterfaceSetHomeProjectCall) DoAndReturn(f func(string)) *MockObsClientInterfaceSetHomeProjectCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// SetProjectMeta mocks base method.
func (m *MockObsClientInterface) SetProjectMeta(meta *common.ProjectMeta) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetProjectMeta", meta)
ret0, _ := ret[0].(error)
return ret0
}
// SetProjectMeta indicates an expected call of SetProjectMeta.
func (mr *MockObsClientInterfaceMockRecorder) SetProjectMeta(meta any) *MockObsClientInterfaceSetProjectMetaCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProjectMeta", reflect.TypeOf((*MockObsClientInterface)(nil).SetProjectMeta), meta)
return &MockObsClientInterfaceSetProjectMetaCall{Call: call}
}
// MockObsClientInterfaceSetProjectMetaCall wrap *gomock.Call
type MockObsClientInterfaceSetProjectMetaCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockObsClientInterfaceSetProjectMetaCall) Return(arg0 error) *MockObsClientInterfaceSetProjectMetaCall {
c.Call = c.Call.Return(arg0)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockObsClientInterfaceSetProjectMetaCall) Do(f func(*common.ProjectMeta) error) *MockObsClientInterfaceSetProjectMetaCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockObsClientInterfaceSetProjectMetaCall) DoAndReturn(f func(*common.ProjectMeta) error) *MockObsClientInterfaceSetProjectMetaCall {
c.Call = c.Call.DoAndReturn(f)
return c
}

View File

@@ -46,6 +46,15 @@ type ObsStatusFetcherWithState interface {
BuildStatusWithState(project string, opts *BuildResultOptions, packages ...string) (*BuildResultList, error)
}
type ObsClientInterface interface {
GetProjectMeta(project string) (*ProjectMeta, error)
SetProjectMeta(meta *ProjectMeta) error
DeleteProject(project string) error
BuildStatus(project string, packages ...string) (*BuildResultList, error)
GetHomeProject() string
SetHomeProject(project string)
}
type ObsClient struct {
baseUrl *url.URL
client *http.Client
@@ -57,6 +66,14 @@ type ObsClient struct {
HomeProject string
}
func (c *ObsClient) GetHomeProject() string {
return c.HomeProject
}
func (c *ObsClient) SetHomeProject(project string) {
c.HomeProject = project
}
func NewObsClient(host string) (*ObsClient, error) {
baseUrl, err := url.Parse(host)
if err != nil {

View File

@@ -50,6 +50,10 @@ const (
var runId uint
var GitWorkTreeAllocate func(string, string, string) (common.GitHandlerGenerator, error) = func(basePath, gitAuthor, email string) (common.GitHandlerGenerator, error) {
return common.AllocateGitWorkTree(basePath, gitAuthor, email)
}
func FetchPrGit(git common.Git, pr *models.PullRequest) error {
// clone PR head via base (target) repo
cloneURL := pr.Base.Repo.CloneURL
@@ -144,9 +148,9 @@ func ProcessBuildStatus(project *common.BuildResultList) BuildStatusSummary {
func ProcessRepoBuildStatus(results []*common.PackageBuildStatus) (status BuildStatusSummary) {
PackageBuildStatusSorter := func(a, b *common.PackageBuildStatus) int {
return strings.Compare(a.Package, b.Package)
}
PackageBuildStatusSorter := func(a, b *common.PackageBuildStatus) int {
return strings.Compare(a.Package, b.Package)
}
common.LogDebug("******* RESULTS: ")
data, _ := xml.MarshalIndent(results, "", " ")
@@ -191,24 +195,23 @@ func GetPackageBuildStatus(project *common.BuildResultList, packageName string)
return true, BuildStatusSummaryUnknown // true for 'missing'
}
// Check for any failures
// Check for any unfinished builds
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
if !res.Finished {
return false, BuildStatusSummaryBuilding
}
}
// Check for any unfinished builds
// Check for any failures
for _, pkgStatus := range packageStatuses {
res, _ := common.ObsBuildStatusDetails[pkgStatus.Code]
// 'ok' is already checked in the loop above
if !res.Finished {
return false, BuildStatusSummaryBuilding
if !res.Success {
return false, BuildStatusSummaryFailed
}
}
@@ -216,7 +219,7 @@ func GetPackageBuildStatus(project *common.BuildResultList, packageName string)
return false, BuildStatusSummarySuccess
}
func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingPrj, buildPrj string, stagingMasterPrj string) (*common.ProjectMeta, error) {
func GenerateObsPrjMeta(obs common.ObsClientInterface, 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)
if err != nil {
@@ -260,13 +263,13 @@ func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque
}
common.LogDebug("Trying first staging master project: ", stagingMasterPrj)
meta, err := ObsClient.GetProjectMeta(stagingMasterPrj)
meta, err := obs.GetProjectMeta(stagingMasterPrj)
if err == nil {
// success, so we use that staging master project as our build project
buildPrj = stagingMasterPrj
} else {
common.LogInfo("error fetching project meta for ", stagingMasterPrj, ". Fall Back to ", buildPrj)
meta, err = ObsClient.GetProjectMeta(buildPrj)
meta, err = obs.GetProjectMeta(buildPrj)
}
if err != nil {
common.LogError("error fetching project meta for", buildPrj, ". Err:", err)
@@ -330,10 +333,10 @@ func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque
// stagingProject:$buildProject
// ^- stagingProject:$buildProject:$subProjectName (based on templateProject)
func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingProject, templateProject, subProjectName string, buildDisableRepos []string) error {
func CreateQASubProject(obs common.ObsClientInterface, stagingConfig *common.StagingConfig, git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingProject, templateProject, subProjectName string, buildDisableRepos []string) error {
common.LogDebug("Setup QA sub projects")
common.LogDebug("reading templateProject ", templateProject)
templateMeta, err := ObsClient.GetProjectMeta(templateProject)
templateMeta, err := obs.GetProjectMeta(templateProject)
if err != nil {
common.LogError("error fetching template project meta for", templateProject, ":", err)
return err
@@ -343,10 +346,10 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
templateMeta.Name = stagingProject + ":" + subProjectName
// freeze tag for now
if len(templateMeta.ScmSync) > 0 {
repository, err := url.Parse(templateMeta.ScmSync)
if err != nil {
panic(err)
}
repository, err := url.Parse(templateMeta.ScmSync)
if err != nil {
panic(err)
}
common.LogDebug("getting data for ", repository.EscapedPath())
split := strings.Split(repository.EscapedPath(), "/")
@@ -354,12 +357,12 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
common.LogDebug("getting commit for ", org, " repo ", repo, " fragment ", repository.Fragment)
branch, err := gitea.GetCommit(org, repo, repository.Fragment)
if err != nil {
panic(err)
}
if err != nil {
panic(err)
}
// set expanded commit url
repository.Fragment = branch.SHA
repository.Fragment = branch.SHA
templateMeta.ScmSync = repository.String()
common.LogDebug("Setting scmsync url to ", templateMeta.ScmSync)
}
@@ -406,11 +409,11 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
templateMeta.Repositories[idx].Paths[pidx].Project = templateMeta.Name
} else
// Check for path prefixes against a template project inside of template project area
if strings.HasPrefix(path.Project, stagingConfig.StagingProject + ":") {
if strings.HasPrefix(path.Project, stagingConfig.StagingProject+":") {
newProjectName := stagingProject
// find project name
for _, setup := range stagingConfig.QA {
if setup.Origin == path.Project {
if setup.Origin == path.Project {
common.LogDebug(" Match:", setup.Origin)
newProjectName = newProjectName + ":" + setup.Name
common.LogDebug(" New:", newProjectName)
@@ -418,14 +421,14 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
}
}
templateMeta.Repositories[idx].Paths[pidx].Project = newProjectName
common.LogDebug(" Matched prefix")
common.LogDebug(" Matched prefix")
}
common.LogDebug(" Path using project ", templateMeta.Repositories[idx].Paths[pidx].Project)
}
}
if !IsDryRun {
err = ObsClient.SetProjectMeta(templateMeta)
err = obs.SetProjectMeta(templateMeta)
if err != nil {
common.LogError("cannot create project:", templateMeta.Name, err)
x, _ := xml.MarshalIndent(templateMeta, "", " ")
@@ -439,10 +442,10 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
return nil
}
func StartOrUpdateBuild(config *common.StagingConfig, git common.Git, gitea common.Gitea, pr *models.PullRequest) (RequestModification, error) {
func StartOrUpdateBuild(obs common.ObsClientInterface, config *common.StagingConfig, git common.Git, gitea common.Gitea, pr *models.PullRequest) (RequestModification, error) {
common.LogDebug("fetching OBS project Meta")
obsPrProject := GetObsProjectAssociatedWithPr(config, ObsClient.HomeProject, pr)
meta, err := ObsClient.GetProjectMeta(obsPrProject)
obsPrProject := GetObsProjectAssociatedWithPr(config, obs.GetHomeProject(), pr)
meta, err := obs.GetProjectMeta(obsPrProject)
if err != nil {
common.LogError("error fetching project meta for", obsPrProject, ":", err)
return RequestModificationNoChange, err
@@ -467,7 +470,7 @@ func StartOrUpdateBuild(config *common.StagingConfig, git common.Git, gitea comm
if meta == nil {
// new build
common.LogDebug(" Staging master:", config.StagingProject)
meta, err = GenerateObsPrjMeta(git, gitea, pr, obsPrProject, config.ObsProject, config.StagingProject)
meta, err = GenerateObsPrjMeta(obs, git, gitea, pr, obsPrProject, config.ObsProject, config.StagingProject)
if err != nil {
return RequestModificationNoChange, err
}
@@ -479,7 +482,7 @@ func StartOrUpdateBuild(config *common.StagingConfig, git common.Git, gitea comm
common.LogDebug("Creating build project:")
common.LogDebug(" meta:", string(x))
} else {
err = ObsClient.SetProjectMeta(meta)
err = obs.SetProjectMeta(meta)
if err != nil {
x, _ := xml.MarshalIndent(meta, "", " ")
common.LogDebug(" meta:", string(x))
@@ -550,7 +553,7 @@ func ParseNotificationToPR(thread *models.NotificationThread) (org string, repo
return
}
func ProcessPullNotification(gitea common.Gitea, thread *models.NotificationThread) {
func ProcessPullNotification(obs common.ObsClientInterface, gitea common.Gitea, thread *models.NotificationThread) {
defer func() {
err := recover()
if err != nil {
@@ -566,7 +569,7 @@ func ProcessPullNotification(gitea common.Gitea, thread *models.NotificationThre
}
common.LogInfo("processing PR:", org, "/", repo, "#", num)
done, err := ProcessPullRequest(gitea, org, repo, num)
done, err := ProcessPullRequest(obs, gitea, org, repo, num)
if !IsDryRun && err == nil && done {
gitea.SetNotificationRead(thread.ID)
} else if err != nil {
@@ -576,7 +579,7 @@ func ProcessPullNotification(gitea common.Gitea, thread *models.NotificationThre
var CleanedUpIssues []int64 = []int64{}
func CleanupPullNotification(gitea common.Gitea, thread *models.NotificationThread) (CleanupComplete bool) {
func CleanupPullNotification(obs common.ObsClientInterface, gitea common.Gitea, thread *models.NotificationThread) (CleanupComplete bool) {
defer func() {
err := recover()
if err != nil {
@@ -643,8 +646,8 @@ func CleanupPullNotification(gitea common.Gitea, thread *models.NotificationThre
return false
}
stagingProject := GetObsProjectAssociatedWithPr(config, ObsClient.HomeProject, pr)
if prj, err := ObsClient.GetProjectMeta(stagingProject); err != nil {
stagingProject := GetObsProjectAssociatedWithPr(config, obs.GetHomeProject(), pr)
if prj, err := obs.GetProjectMeta(stagingProject); err != nil {
common.LogError("Failed fetching meta for project:", stagingProject, ". Not cleaning up")
return false
} else if prj == nil && err == nil {
@@ -658,13 +661,13 @@ func CleanupPullNotification(gitea common.Gitea, thread *models.NotificationThre
project := stagingProject + ":" + qa.Name
common.LogDebug("Cleaning up QA staging", project)
if !IsDryRun {
if err := ObsClient.DeleteProject(project); err != nil {
if err := obs.DeleteProject(project); err != nil {
common.LogError("Failed to cleanup QA staging", project, err)
}
}
}
if !IsDryRun {
if err := ObsClient.DeleteProject(stagingProject); err != nil {
if err := obs.DeleteProject(stagingProject); err != nil {
common.LogError("Failed to cleanup staging", stagingProject, err)
}
}
@@ -685,7 +688,7 @@ 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) {
func CommentPROnce(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
@@ -697,6 +700,18 @@ func commentOnPackagePR(gitea common.Gitea, org string, repo string, prNum int64
return
}
timeline, err := gitea.GetTimeline(org, repo, prNum)
if err != nil {
common.LogError("Failed to get timeline for PR %s/%s#%d: %v", org, repo, prNum, err)
return
}
for _, t := range timeline {
if t.User != nil && t.User.UserName == BotUser && t.Type == common.TimelineCommentType_Comment && t.Body == msg {
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)
@@ -704,20 +719,21 @@ func commentOnPackagePR(gitea common.Gitea, org string, repo string, prNum int64
}
// Create and remove QA projects
func ProcessQaProjects(stagingConfig *common.StagingConfig, git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingProject string) []string {
func ProcessQaProjects(obs common.ObsClientInterface, stagingConfig *common.StagingConfig, git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingProject string) ([]string, string) {
usedQAprojects := make([]string, 0)
prLabelNames := make(map[string]int)
for _, label := range pr.Labels {
prLabelNames[label.Name] = 1
}
msg := ""
var qa_projects []string
for _, setup := range stagingConfig.QA {
QAproject := stagingProject + ":" + setup.Name
if len(setup.Label) > 0 {
if _, ok := prLabelNames[setup.Label]; !ok {
if !IsDryRun {
// blindly remove, will fail when not existing
ObsClient.DeleteProject(QAproject)
obs.DeleteProject(QAproject)
}
common.LogInfo("QA project ", setup.Name, "has no matching Label")
continue
@@ -726,24 +742,25 @@ func ProcessQaProjects(stagingConfig *common.StagingConfig, git common.Git, gite
usedQAprojects = append(usedQAprojects, QAproject)
// check for existens first, no error, but no meta is a 404
if meta, err := ObsClient.GetProjectMeta(QAproject); meta == nil && err == nil {
if meta, err := obs.GetProjectMeta(QAproject); meta == nil && err == nil {
common.LogInfo("Create QA project ", QAproject)
CreateQASubProject(stagingConfig, git, gitea, pr,
CreateQASubProject(obs, stagingConfig, git, gitea, pr,
stagingProject,
setup.Origin,
setup.Name,
setup.BuildDisableRepos)
msg = msg + "QA Project added: " + ObsWebHost + "/project/show/" +
QAproject + "\n"
qa_projects = append(qa_projects, ObsWebHost+"/project/show/"+QAproject)
}
}
if len(msg) > 1 {
gitea.AddComment(pr, msg)
if len(qa_projects) > 0 {
msg = "Additional QA builds:\n" + strings.Join(qa_projects, "\n")
}
return usedQAprojects
return usedQAprojects, msg
}
func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, error) {
func ProcessPullRequest(obs common.ObsClientInterface, gitea common.Gitea, org, repo string, id int64) (bool, error) {
dir, err := os.MkdirTemp(os.TempDir(), BotName)
common.PanicOnError(err)
if IsDryRun {
@@ -752,7 +769,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
defer os.RemoveAll(dir)
}
gh, err := common.AllocateGitWorkTree(dir, GitAuthor, "noaddress@suse.de")
gh, err := GitWorkTreeAllocate(dir, GitAuthor, "noaddress@suse.de")
common.PanicOnError(err)
git, err := gh.CreateGitHandler(org)
@@ -797,7 +814,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
if err != nil {
common.LogError("Staging config", common.StagingConfigFile, "not found in PR to the project. Aborting.")
if !IsDryRun {
_, err = gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot find project config in PR: "+common.ProjectConfigFile)
_, _ = gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot find project config in PR: "+common.ProjectConfigFile)
}
return true, err
}
@@ -817,7 +834,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
return true, nil
}
meta, err := ObsClient.GetProjectMeta(stagingConfig.ObsProject)
meta, err := obs.GetProjectMeta(stagingConfig.ObsProject)
if err != nil || meta == nil {
common.LogError("Cannot find reference project meta:", stagingConfig.ObsProject, err)
if !IsDryRun && err == nil {
@@ -946,8 +963,8 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
}
common.LogDebug("ObsProject:", stagingConfig.ObsProject)
stagingProject := GetObsProjectAssociatedWithPr(stagingConfig, ObsClient.HomeProject, pr)
change, err := StartOrUpdateBuild(stagingConfig, git, gitea, pr)
stagingProject := GetObsProjectAssociatedWithPr(stagingConfig, obs.GetHomeProject(), pr)
change, err := StartOrUpdateBuild(obs, stagingConfig, git, gitea, pr)
status := &models.CommitStatus{
Context: BotName,
Description: "OBS Staging build",
@@ -978,11 +995,8 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
SetStatus(gitea, org, repo, pr.Head.Sha, status)
}
if change != RequestModificationNoChange && !IsDryRun {
gitea.AddComment(pr, msg)
}
stagingResult, err := ObsClient.BuildStatus(stagingProject)
stagingResult, err := obs.BuildStatus(stagingProject)
if err != nil {
common.LogError("failed fetching stage project status for", stagingProject, ":", err)
}
@@ -990,7 +1004,14 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
_, packagePRs := common.ExtractDescriptionAndPRs(bufio.NewScanner(strings.NewReader(pr.Body)))
// always update QA projects because Labels can change
qaProjects := ProcessQaProjects(stagingConfig, git, gitea, pr, stagingProject)
qaProjects, qaProjectMsg := ProcessQaProjects(obs, stagingConfig, git, gitea, pr, stagingProject)
if change != RequestModificationNoChange && !IsDryRun {
if len(qaProjectMsg) > 0 {
msg += "\n" + qaProjectMsg
}
CommentPROnce(gitea, org, repo, id, msg)
}
done := false
overallBuildStatus := ProcessBuildStatus(stagingResult)
@@ -998,7 +1019,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
if len(qaProjects) > 0 && overallBuildStatus == BuildStatusSummarySuccess {
seperator := " in "
for _, qaProject := range qaProjects {
qaResult, err := ObsClient.BuildStatus(qaProject)
qaResult, err := obs.BuildStatus(qaProject)
if err != nil {
common.LogError("failed fetching stage project status for", qaProject, ":", err)
}
@@ -1058,7 +1079,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
default:
continue
}
commentOnPackagePR(gitea, packagePR.Org, packagePR.Repo, packagePR.Num, msg)
CommentPROnce(gitea, packagePR.Org, packagePR.Repo, packagePR.Num, msg)
}
if len(missingPkgs) > 0 {
@@ -1068,10 +1089,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
msg = msg + " - " + pkg + "\n"
}
common.LogInfo(msg)
err := gitea.AddComment(pr, msg)
if err != nil {
common.LogError(err)
}
CommentPROnce(gitea, org, repo, id, msg)
}
}
@@ -1090,8 +1108,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
return false, nil
}
func PollWorkNotifications(giteaUrl string) {
gitea := common.AllocateGiteaTransport(giteaUrl)
func PollWorkNotifications(obs common.ObsClientInterface, gitea common.Gitea) {
data, err := gitea.GetNotifications(common.GiteaNotificationType_Pull, nil)
if err != nil {
@@ -1107,7 +1124,7 @@ func PollWorkNotifications(giteaUrl string) {
if !ListPullNotificationsOnly {
switch notification.Subject.Type {
case "Pull":
ProcessPullNotification(gitea, notification)
ProcessPullNotification(obs, gitea, notification)
default:
if !IsDryRun {
gitea.SetNotificationRead(notification.ID)
@@ -1130,7 +1147,7 @@ func PollWorkNotifications(giteaUrl string) {
continue
}
cleanupFinished = CleanupPullNotification(gitea, n) && cleanupFinished
cleanupFinished = CleanupPullNotification(obs, gitea, n) && cleanupFinished
}
} else if err != nil {
common.LogError(err)
@@ -1144,7 +1161,8 @@ var ObsApiHost string
var ObsWebHost string
var IsDryRun bool
var ProcessPROnly string
var ObsClient *common.ObsClient
var ObsClient common.ObsClientInterface
var BotUser string
func ObsWebHostFromApiHost(apihost string) string {
u, err := url.Parse(apihost)
@@ -1209,9 +1227,18 @@ func main() {
}
if len(*buildRoot) > 0 {
ObsClient.HomeProject = *buildRoot
ObsClient.SetHomeProject(*buildRoot)
}
gitea := common.AllocateGiteaTransport(GiteaUrl)
user, err := gitea.GetCurrentUser()
if err != nil {
common.LogError("Cannot fetch current user:", err)
return
}
BotUser = user.UserName
if len(*ProcessPROnly) > 0 {
rx := regexp.MustCompile("^([^/#]+)/([^/#]+)#([0-9]+)$")
m := rx.FindStringSubmatch(*ProcessPROnly)
@@ -1220,15 +1247,14 @@ func main() {
return
}
gitea := common.AllocateGiteaTransport(GiteaUrl)
id, _ := strconv.ParseInt(m[3], 10, 64)
ProcessPullRequest(gitea, m[1], m[2], id)
ProcessPullRequest(ObsClient, gitea, m[1], m[2], id)
return
}
for {
PollWorkNotifications(GiteaUrl)
PollWorkNotifications(ObsClient, gitea)
common.LogInfo("Poll cycle finished")
time.Sleep(5 * time.Minute)
}

File diff suppressed because it is too large Load Diff