1 Commits
main ... main

Author SHA256 Message Date
Michal Suchanek
f878e4895e Make it possible to specify BR format in query string
instead of

curl -H 'accept: application/obs+xml' https://br.opensuse.org/status/Java:packages/jameica
curl -H 'accept: application/json' https://br.opensuse.org/status/Java:packages/jameica

this can be used

curl https://br.opensuse.org/status/Java:packages/jameica?format=xml
curl https://br.opensuse.org/status/Java:packages/jameica?format=json

which provides much simpler and easier to understand interface.

Fixes: #94
2025-11-06 16:56:09 +01:00
20 changed files with 93 additions and 604 deletions

View File

@@ -61,20 +61,6 @@ type Permissions struct {
Members []string
}
const (
Label_StagingAuto = "staging/Auto"
Label_ReviewPending = "review/Pending"
Label_ReviewDone = "review/Done"
)
func LabelKey(tag_value string) string {
// capitalize first letter and remove /
if len(tag_value) == 0 {
return ""
}
return strings.ToUpper(tag_value[0:1]) + strings.ReplaceAll(tag_value[1:], "/", "")
}
type AutogitConfig struct {
Workflows []string // [pr, direct, test]
Organization string
@@ -86,8 +72,6 @@ type AutogitConfig struct {
Committers []string // group in addition to Reviewers and Maintainers that can order the bot around, mostly as helper for factory-maintainers
Subdirs []string // list of directories to sort submodules into. Needed b/c _manifest cannot list non-existent directories
Labels map[string]string // list of tags, if not default, to apply
NoProjectGitPR bool // do not automatically create project git PRs, just assign reviewers and assume somethign else creates the ProjectGit PR
ManualMergeOnly bool // only merge with "Merge OK" comment by Project Maintainers and/or Package Maintainers and/or reviewers
ManualMergeProject bool // require merge of ProjectGit PRs with "Merge OK" by ProjectMaintainers and/or reviewers
@@ -204,8 +188,6 @@ func (configs AutogitConfigs) GetPrjGitConfig(org, repo, branch string) *Autogit
if c.GitProjectName == prjgit {
return c
}
}
for _, c := range configs {
if c.Organization == org && c.Branch == branch {
return c
}
@@ -291,14 +273,6 @@ func (config *AutogitConfig) GetRemoteBranch() string {
return "origin_" + config.Branch
}
func (config *AutogitConfig) Label(label string) string {
if t, found := config.Labels[LabelKey(label)]; found {
return t
}
return label
}
type StagingConfig struct {
ObsProject string
RebuildAll bool

View File

@@ -10,67 +10,6 @@ import (
mock_common "src.opensuse.org/autogits/common/mock"
)
func TestLabelKey(t *testing.T) {
tests := map[string]string{
"": "",
"foo": "Foo",
"foo/bar": "Foobar",
"foo/Bar": "FooBar",
}
for k, v := range tests {
if c := common.LabelKey(k); c != v {
t.Error("expected", v, "got", c, "input", k)
}
}
}
func TestConfigLabelParser(t *testing.T) {
tests := []struct {
name string
json string
label_value string
}{
{
name: "empty",
json: "{}",
label_value: "path/String",
},
{
name: "defined",
json: `{"Labels": {"foo": "bar", "PathString": "moo/Label"}}`,
label_value: "moo/Label",
},
{
name: "undefined",
json: `{"Labels": {"foo": "bar", "NotPathString": "moo/Label"}}`,
label_value: "path/String",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
repo := models.Repository{
DefaultBranch: "master",
}
ctl := gomock.NewController(t)
gitea := mock_common.NewMockGiteaFileContentAndRepoFetcher(ctl)
gitea.EXPECT().GetRepositoryFileContent("foo", "bar", "", "workflow.config").Return([]byte(test.json), "abc", nil)
gitea.EXPECT().GetRepository("foo", "bar").Return(&repo, nil)
config, err := common.ReadWorkflowConfig(gitea, "foo/bar")
if err != nil || config == nil {
t.Fatal(err)
}
if l := config.Label("path/String"); l != test.label_value {
t.Error("Expecting", test.label_value, "got", l)
}
})
}
}
func TestProjectConfigMatcher(t *testing.T) {
configs := common.AutogitConfigs{
{
@@ -82,15 +21,6 @@ func TestProjectConfigMatcher(t *testing.T) {
Branch: "main",
GitProjectName: "test/prjgit#main",
},
{
Organization: "test",
Branch: "main",
GitProjectName: "test/bar#never_match",
},
{
Organization: "test",
GitProjectName: "test/bar#main",
},
}
tests := []struct {
@@ -120,20 +50,6 @@ func TestProjectConfigMatcher(t *testing.T) {
branch: "main",
config: 1,
},
{
name: "prjgit only match",
org: "test",
repo: "bar",
branch: "main",
config: 3,
},
{
name: "non-default branch match",
org: "test",
repo: "bar",
branch: "something_main",
config: -1,
},
}
for _, test := range tests {
@@ -189,10 +105,6 @@ func TestConfigWorkflowParser(t *testing.T) {
if config.ManualMergeOnly != false {
t.Fatal("This should be false")
}
if config.Label("foobar") != "foobar" {
t.Fatal("undefined label should return default value")
}
})
}
}

View File

@@ -350,10 +350,6 @@ var ExtraGitParams []string
func (e *GitHandlerImpl) GitExecWithOutput(cwd string, params ...string) (string, error) {
cmd := exec.Command("/usr/bin/git", params...)
var identityFile string
if i := os.Getenv("AUTOGITS_IDENTITY_FILE"); len(i) > 0 {
identityFile = " -i " + i
}
cmd.Env = []string{
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
"GIT_CONFIG_GLOBAL=/dev/null",
@@ -362,7 +358,7 @@ func (e *GitHandlerImpl) GitExecWithOutput(cwd string, params ...string) (string
"EMAIL=not@exist@src.opensuse.org",
"GIT_LFS_SKIP_SMUDGE=1",
"GIT_LFS_SKIP_PUSH=1",
"GIT_SSH_COMMAND=/usr/bin/ssh -o StrictHostKeyChecking=yes" + identityFile,
"GIT_SSH_COMMAND=/usr/bin/ssh -o StrictHostKeyChecking=yes",
}
if len(ExtraGitParams) > 0 {
cmd.Env = append(cmd.Env, ExtraGitParams...)

View File

@@ -29,7 +29,6 @@ import (
"path"
"path/filepath"
"slices"
"sync"
"time"
transport "github.com/go-openapi/runtime/client"
@@ -67,14 +66,6 @@ const (
ReviewStateUnknown models.ReviewStateType = ""
)
type GiteaLabelGetter interface {
GetLabels(org, repo string, idx int64) ([]*models.Label, error)
}
type GiteaLabelSettter interface {
SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error)
}
type GiteaTimelineFetcher interface {
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
}
@@ -191,8 +182,6 @@ type Gitea interface {
GiteaCommitStatusGetter
GiteaCommitStatusSetter
GiteaSetRepoOptions
GiteaLabelGetter
GiteaLabelSettter
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
@@ -200,7 +189,7 @@ type Gitea interface {
GetOrganization(orgName string) (*models.Organization, error)
GetOrganizationRepositories(orgName string) ([]*models.Repository, error)
CreateRepositoryIfNotExist(git Git, org, repoName string) (*models.Repository, error)
CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error, bool)
CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error)
GetPullRequestFileContent(pr *models.PullRequest, path string) ([]byte, string, error)
GetRecentPullRequests(org, repo, branch string) ([]*models.PullRequest, error)
GetRecentCommits(org, repo, branch string, commitNo int64) ([]*models.Commit, error)
@@ -477,30 +466,6 @@ func (gitea *GiteaTransport) SetRepoOptions(owner, repo string, manual_merge boo
return ok.Payload, err
}
func (gitea *GiteaTransport) GetLabels(owner, repo string, idx int64) ([]*models.Label, error) {
ret, err := gitea.client.Issue.IssueGetLabels(issue.NewIssueGetLabelsParams().WithOwner(owner).WithRepo(repo).WithIndex(idx), gitea.transport.DefaultAuthentication)
if err != nil {
return nil, err
}
return ret.Payload, err
}
func (gitea *GiteaTransport) SetLabels(owner, repo string, idx int64, labels []string) ([]*models.Label, error) {
interfaceLabels := make([]interface{}, len(labels))
for i, l := range labels {
interfaceLabels[i] = l
}
ret, err := gitea.client.Issue.IssueAddLabel(issue.NewIssueAddLabelParams().WithOwner(owner).WithRepo(repo).WithIndex(idx).WithBody(&models.IssueLabelsOption{Labels: interfaceLabels}),
gitea.transport.DefaultAuthentication)
if err != nil {
return nil, err
}
return ret.Payload, nil
}
const (
GiteaNotificationType_Pull = "Pull"
)
@@ -678,7 +643,7 @@ func (gitea *GiteaTransport) CreateRepositoryIfNotExist(git Git, org, repoName s
return repo.Payload, nil
}
func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error, bool) {
func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error) {
prOptions := models.CreatePullRequestOption{
Base: targetId,
Head: srcId,
@@ -694,7 +659,7 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
WithHead(srcId),
gitea.transport.DefaultAuthentication,
); err == nil && pr.Payload.State == "open" {
return pr.Payload, nil, false
return pr.Payload, nil
}
pr, err := gitea.client.Repository.RepoCreatePullRequest(
@@ -708,10 +673,10 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
)
if err != nil {
return nil, fmt.Errorf("Cannot create pull request. %w", err), true
return nil, fmt.Errorf("Cannot create pull request. %w", err)
}
return pr.GetPayload(), nil, true
return pr.GetPayload(), nil
}
func (gitea *GiteaTransport) RequestReviews(pr *models.PullRequest, reviewers ...string) ([]*models.PullReview, error) {
@@ -798,78 +763,45 @@ func (gitea *GiteaTransport) AddComment(pr *models.PullRequest, comment string)
return nil
}
type TimelineCacheData struct {
data []*models.TimelineComment
lastCheck time.Time
}
var giteaTimelineCache map[string]TimelineCacheData = make(map[string]TimelineCacheData)
var giteaTimelineCacheMutex sync.RWMutex
func (gitea *GiteaTransport) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
page := int64(1)
resCount := 1
prID := fmt.Sprintf("%s/%s!%d", org, repo, idx)
giteaTimelineCacheMutex.RLock()
TimelineCache, IsCached := giteaTimelineCache[prID]
var LastCachedTime strfmt.DateTime
if IsCached {
l := len(TimelineCache.data)
if l > 0 {
LastCachedTime = TimelineCache.data[0].Updated
}
// cache data for 5 seconds
if TimelineCache.lastCheck.Add(time.Second*5).Compare(time.Now()) > 0 {
giteaTimelineCacheMutex.RUnlock()
return TimelineCache.data, nil
}
}
giteaTimelineCacheMutex.RUnlock()
giteaTimelineCacheMutex.Lock()
defer giteaTimelineCacheMutex.Unlock()
retData := []*models.TimelineComment{}
for resCount > 0 {
opts := issue.NewIssueGetCommentsAndTimelineParams().WithOwner(org).WithRepo(repo).WithIndex(idx).WithPage(&page)
if !LastCachedTime.IsZero() {
opts = opts.WithSince(&LastCachedTime)
}
res, err := gitea.client.Issue.IssueGetCommentsAndTimeline(opts, gitea.transport.DefaultAuthentication)
res, err := gitea.client.Issue.IssueGetCommentsAndTimeline(
issue.NewIssueGetCommentsAndTimelineParams().
WithOwner(org).
WithRepo(repo).
WithIndex(idx).
WithPage(&page),
gitea.transport.DefaultAuthentication,
)
if err != nil {
return nil, err
}
if resCount = len(res.Payload); resCount == 0 {
break
}
for _, d := range res.Payload {
if d != nil {
if time.Time(d.Created).Compare(time.Time(LastCachedTime)) > 0 {
// created after last check, so we append here
TimelineCache.data = append(TimelineCache.data, d)
} else {
// we need something updated in the timeline, maybe
}
}
}
if resCount < 10 {
resCount = len(res.Payload)
LogDebug("page:", page, "len:", resCount)
if resCount == 0 {
break
}
page++
for _, d := range res.Payload {
if d != nil {
retData = append(retData, d)
}
}
}
LogDebug("timeline", prID, "# timeline:", len(TimelineCache.data))
slices.SortFunc(TimelineCache.data, func(a, b *models.TimelineComment) int {
LogDebug("total results:", len(retData))
slices.SortFunc(retData, func(a, b *models.TimelineComment) int {
return time.Time(b.Created).Compare(time.Time(a.Created))
})
TimelineCache.lastCheck = time.Now()
giteaTimelineCache[prID] = TimelineCache
return TimelineCache.data, nil
return retData, nil
}
func (gitea *GiteaTransport) GetRepositoryFileContent(org, repo, hash, path string) ([]byte, string, error) {

View File

@@ -18,132 +18,6 @@ import (
models "src.opensuse.org/autogits/common/gitea-generated/models"
)
// MockGiteaLabelGetter is a mock of GiteaLabelGetter interface.
type MockGiteaLabelGetter struct {
ctrl *gomock.Controller
recorder *MockGiteaLabelGetterMockRecorder
isgomock struct{}
}
// MockGiteaLabelGetterMockRecorder is the mock recorder for MockGiteaLabelGetter.
type MockGiteaLabelGetterMockRecorder struct {
mock *MockGiteaLabelGetter
}
// NewMockGiteaLabelGetter creates a new mock instance.
func NewMockGiteaLabelGetter(ctrl *gomock.Controller) *MockGiteaLabelGetter {
mock := &MockGiteaLabelGetter{ctrl: ctrl}
mock.recorder = &MockGiteaLabelGetterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockGiteaLabelGetter) EXPECT() *MockGiteaLabelGetterMockRecorder {
return m.recorder
}
// GetLabels mocks base method.
func (m *MockGiteaLabelGetter) GetLabels(org, repo string, idx int64) ([]*models.Label, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetLabels", org, repo, idx)
ret0, _ := ret[0].([]*models.Label)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetLabels indicates an expected call of GetLabels.
func (mr *MockGiteaLabelGetterMockRecorder) GetLabels(org, repo, idx any) *MockGiteaLabelGetterGetLabelsCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLabels", reflect.TypeOf((*MockGiteaLabelGetter)(nil).GetLabels), org, repo, idx)
return &MockGiteaLabelGetterGetLabelsCall{Call: call}
}
// MockGiteaLabelGetterGetLabelsCall wrap *gomock.Call
type MockGiteaLabelGetterGetLabelsCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaLabelGetterGetLabelsCall) Return(arg0 []*models.Label, arg1 error) *MockGiteaLabelGetterGetLabelsCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaLabelGetterGetLabelsCall) Do(f func(string, string, int64) ([]*models.Label, error)) *MockGiteaLabelGetterGetLabelsCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaLabelGetterGetLabelsCall) DoAndReturn(f func(string, string, int64) ([]*models.Label, error)) *MockGiteaLabelGetterGetLabelsCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaLabelSettter is a mock of GiteaLabelSettter interface.
type MockGiteaLabelSettter struct {
ctrl *gomock.Controller
recorder *MockGiteaLabelSettterMockRecorder
isgomock struct{}
}
// MockGiteaLabelSettterMockRecorder is the mock recorder for MockGiteaLabelSettter.
type MockGiteaLabelSettterMockRecorder struct {
mock *MockGiteaLabelSettter
}
// NewMockGiteaLabelSettter creates a new mock instance.
func NewMockGiteaLabelSettter(ctrl *gomock.Controller) *MockGiteaLabelSettter {
mock := &MockGiteaLabelSettter{ctrl: ctrl}
mock.recorder = &MockGiteaLabelSettterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockGiteaLabelSettter) EXPECT() *MockGiteaLabelSettterMockRecorder {
return m.recorder
}
// SetLabels mocks base method.
func (m *MockGiteaLabelSettter) SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetLabels", org, repo, idx, labels)
ret0, _ := ret[0].([]*models.Label)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SetLabels indicates an expected call of SetLabels.
func (mr *MockGiteaLabelSettterMockRecorder) SetLabels(org, repo, idx, labels any) *MockGiteaLabelSettterSetLabelsCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLabels", reflect.TypeOf((*MockGiteaLabelSettter)(nil).SetLabels), org, repo, idx, labels)
return &MockGiteaLabelSettterSetLabelsCall{Call: call}
}
// MockGiteaLabelSettterSetLabelsCall wrap *gomock.Call
type MockGiteaLabelSettterSetLabelsCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaLabelSettterSetLabelsCall) Return(arg0 []*models.Label, arg1 error) *MockGiteaLabelSettterSetLabelsCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaLabelSettterSetLabelsCall) Do(f func(string, string, int64, []string) ([]*models.Label, error)) *MockGiteaLabelSettterSetLabelsCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaLabelSettterSetLabelsCall) DoAndReturn(f func(string, string, int64, []string) ([]*models.Label, error)) *MockGiteaLabelSettterSetLabelsCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// MockGiteaTimelineFetcher is a mock of GiteaTimelineFetcher interface.
type MockGiteaTimelineFetcher struct {
ctrl *gomock.Controller
@@ -1976,13 +1850,12 @@ func (c *MockGiteaAddReviewCommentCall) DoAndReturn(f func(*models.PullRequest,
}
// CreatePullRequestIfNotExist mocks base method.
func (m *MockGitea) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error, bool) {
func (m *MockGitea) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreatePullRequestIfNotExist", repo, srcId, targetId, title, body)
ret0, _ := ret[0].(*models.PullRequest)
ret1, _ := ret[1].(error)
ret2, _ := ret[2].(bool)
return ret0, ret1, ret2
return ret0, ret1
}
// CreatePullRequestIfNotExist indicates an expected call of CreatePullRequestIfNotExist.
@@ -1998,19 +1871,19 @@ type MockGiteaCreatePullRequestIfNotExistCall struct {
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaCreatePullRequestIfNotExistCall) Return(arg0 *models.PullRequest, arg1 error, arg2 bool) *MockGiteaCreatePullRequestIfNotExistCall {
c.Call = c.Call.Return(arg0, arg1, arg2)
func (c *MockGiteaCreatePullRequestIfNotExistCall) Return(arg0 *models.PullRequest, arg1 error) *MockGiteaCreatePullRequestIfNotExistCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaCreatePullRequestIfNotExistCall) Do(f func(*models.Repository, string, string, string, string) (*models.PullRequest, error, bool)) *MockGiteaCreatePullRequestIfNotExistCall {
func (c *MockGiteaCreatePullRequestIfNotExistCall) Do(f func(*models.Repository, string, string, string, string) (*models.PullRequest, error)) *MockGiteaCreatePullRequestIfNotExistCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaCreatePullRequestIfNotExistCall) DoAndReturn(f func(*models.Repository, string, string, string, string) (*models.PullRequest, error, bool)) *MockGiteaCreatePullRequestIfNotExistCall {
func (c *MockGiteaCreatePullRequestIfNotExistCall) DoAndReturn(f func(*models.Repository, string, string, string, string) (*models.PullRequest, error)) *MockGiteaCreatePullRequestIfNotExistCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
@@ -2329,45 +2202,6 @@ func (c *MockGiteaGetIssueCommentsCall) DoAndReturn(f func(string, string, int64
return c
}
// GetLabels mocks base method.
func (m *MockGitea) GetLabels(org, repo string, idx int64) ([]*models.Label, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetLabels", org, repo, idx)
ret0, _ := ret[0].([]*models.Label)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetLabels indicates an expected call of GetLabels.
func (mr *MockGiteaMockRecorder) GetLabels(org, repo, idx any) *MockGiteaGetLabelsCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLabels", reflect.TypeOf((*MockGitea)(nil).GetLabels), org, repo, idx)
return &MockGiteaGetLabelsCall{Call: call}
}
// MockGiteaGetLabelsCall wrap *gomock.Call
type MockGiteaGetLabelsCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaGetLabelsCall) Return(arg0 []*models.Label, arg1 error) *MockGiteaGetLabelsCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaGetLabelsCall) Do(f func(string, string, int64) ([]*models.Label, error)) *MockGiteaGetLabelsCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaGetLabelsCall) DoAndReturn(f func(string, string, int64) ([]*models.Label, error)) *MockGiteaGetLabelsCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// GetNotifications mocks base method.
func (m *MockGitea) GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error) {
m.ctrl.T.Helper()
@@ -2959,45 +2793,6 @@ func (c *MockGiteaSetCommitStatusCall) DoAndReturn(f func(string, string, string
return c
}
// SetLabels mocks base method.
func (m *MockGitea) SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetLabels", org, repo, idx, labels)
ret0, _ := ret[0].([]*models.Label)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SetLabels indicates an expected call of SetLabels.
func (mr *MockGiteaMockRecorder) SetLabels(org, repo, idx, labels any) *MockGiteaSetLabelsCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLabels", reflect.TypeOf((*MockGitea)(nil).SetLabels), org, repo, idx, labels)
return &MockGiteaSetLabelsCall{Call: call}
}
// MockGiteaSetLabelsCall wrap *gomock.Call
type MockGiteaSetLabelsCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGiteaSetLabelsCall) Return(arg0 []*models.Label, arg1 error) *MockGiteaSetLabelsCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGiteaSetLabelsCall) Do(f func(string, string, int64, []string) ([]*models.Label, error)) *MockGiteaSetLabelsCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGiteaSetLabelsCall) DoAndReturn(f func(string, string, int64, []string) ([]*models.Label, error)) *MockGiteaSetLabelsCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// SetNotificationRead mocks base method.
func (m *MockGitea) SetNotificationRead(notificationId int64) error {
m.ctrl.T.Helper()

View File

@@ -233,11 +233,10 @@ next_rs:
}
for _, pr := range prjpr_set {
if strings.EqualFold(prinfo.PR.Base.Repo.Owner.UserName, pr.Org) && strings.EqualFold(prinfo.PR.Base.Repo.Name, pr.Repo) && prinfo.PR.Index == pr.Num {
if prinfo.PR.Base.Repo.Owner.UserName == pr.Org && prinfo.PR.Base.Repo.Name == pr.Repo && prinfo.PR.Index == pr.Num {
continue next_rs
}
}
LogDebug(" PR: ", PRtoString(prinfo.PR), "not found in project git PRSet")
return false
}
return true
@@ -294,7 +293,7 @@ func (rs *PRSet) AssignReviewers(gitea GiteaReviewFetcherAndRequester, maintaine
if !IsDryRun {
for _, r := range reviewers {
if _, err := gitea.RequestReviews(pr.PR, r); err != nil {
LogError("Cannot create reviews on", fmt.Sprintf("%s/%s!%d for [%s]", pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index, r), err)
LogError("Cannot create reviews on", fmt.Sprintf("%s/%s!%d for [%s]", pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index, strings.Join(reviewers, ", ")), err)
}
}
}

View File

@@ -58,30 +58,6 @@ sub ListPackages {
return @packages;
}
sub FactoryMd5 {
my ($package) = @_;
my $out = "";
if (system("osc ls openSUSE:Factory $package | grep -q build.specials.obscpio") == 0) {
system("mkdir _extract") == 0 || die "_extract exists or can't make it. Aborting.";
chdir("_extract") || die;
system("osc cat openSUSE:Factory $package build.specials.obscpio | cpio -dium 2> /dev/null") == 0 || die;
system("rm .* 2> /dev/null");
open( my $fh, "find -type f -exec /usr/bin/basename {} \\; | xargs md5sum | awk '{print \$1 FS \$2}' | grep -v d41d8cd98f00b204e9800998ecf8427e |") or die;
while ( my $l = <$fh>) {
$out = $out.$l;
}
close($fh);
chdir("..") && system("rm -rf _extract") == 0 || die;
}
open( my $fh, "osc ls -v openSUSE:Factory $package | awk '{print \$1 FS \$7}' | grep -v -F '_scmsync.obsinfo\nbuild.specials.obscpio' |") or die;
while (my $l = <$fh>) {
$out = $out.$l;
}
close($fh);
return $out;
}
# Read project from first argument
sub Usage {
die "Usage: $0 <OBS Project> [org [package]]";
@@ -89,7 +65,6 @@ sub Usage {
my $project = shift or Usage();
my $org = shift;
if (not defined($org)) {
$org = `osc meta prj $project | grep scmsync | sed -e 's,^.*src.opensuse.org/\\(.*\\)/_ObsPrj.*,\\1,'`;
chomp($org);
@@ -164,7 +139,7 @@ if ( scalar @tomove > 0 ) {
system("git -C $pkg push origin factory") == 0 and
system("git obs $super_user api -X PATCH --data '{\"default_branch\": \"factory\"}' /repos/pool/$pkg") == 0
or die "Error in creating a pool repo";
system("for i in \$(git -C $pkg for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin/ | grep -v '\\(^HEAD\$\\|^factory\$\\)'); do git -C $pkg push origin :\$i; done") == 0 or die "failed to cull branches";
system("for i in \$(git for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin/ | grep -v '\\(^HEAD\$\\|^factory\$\)'); do git -C $pkg push origin :\$i; done") == 0 or die "failed to cull branches";
}
}
@@ -192,7 +167,6 @@ for my $package ( sort(@packages) ) {
or ( push( @tomove, $package ) and die "Can't fetch pool for $package" );
my @commits = FindFactoryCommit($package);
my $Md5Hashes = FactoryMd5($package);
my $c;
my $match = 0;
for my $commit (@commits) {
@@ -205,27 +179,16 @@ for my $package ( sort(@packages) ) {
system("git -C $package lfs fetch pool $commit") == 0
and system("git -C $package checkout -B factory $commit") == 0
and system("git -C $package lfs checkout") == 0
and chdir($package)) {
open(my $fh, "|-", "md5sum -c --quiet") or die $!;
print $fh $Md5Hashes;
close $fh;
if ($? >> 8 != 0) {
chdir("..") || die;
next;
}
open($fh, "|-", "awk '{print \$2}' | sort | bash -c \"diff <(ls -1 | sort) -\"") or die $!;
print $fh $Md5Hashes;
close $fh;
my $ec = $? >> 8;
chdir("..") || die;
if ($ec == 0) {
$c = $commit;
$match = 1;
last;
}
and system(
"cd $package; osc ls -v openSUSE:Factory $package | awk '{print \$1 FS \$7}' | grep -v -F '_scmsync.obsinfo\nbuild.specials.obscpio' | md5sum -c --quiet"
) == 0
and system("bash -c \"diff <(ls -1 $package | sort) <(osc ls openSUSE:Factory $package | grep -v -F '_scmsync.obsinfo\nbuild.specials.obscpio' | sort)\"") == 0
)
{
$c = $commit;
$match = 1;
last;
}
}

View File

@@ -149,7 +149,7 @@ func ProcessNotifications(notification *models.NotificationThread, gitea common.
}
}()
rx := regexp.MustCompile(`^/?api/v\d+/repos/(?<org>[_\.a-zA-Z0-9-]+)/(?<project>[_\.a-zA-Z0-9-]+)/(?:issues|pulls)/(?<num>[0-9]+)$`)
rx := regexp.MustCompile(`^/?api/v\d+/repos/(?<org>[_a-zA-Z0-9-]+)/(?<project>[_a-zA-Z0-9-]+)/(?:issues|pulls)/(?<num>[0-9]+)$`)
subject := notification.Subject
u, err := url.Parse(notification.Subject.URL)
if err != nil {

View File

@@ -1,10 +1,7 @@
OBS Status Service
==================
Reports build status of OBS service as an easily to produce SVG. Repository
results (build results) are cached for 10 seconds and repository listing
for OBS instance are cached for 5 minutes -- new repositories take up to
5 minutes to be visible.
Reports build status of OBS service as an easily to produce SVG
Requests for individual build results:
@@ -20,32 +17,19 @@ Get requests for / will also return 404 statu normally. If the Backend redis
server is not available, it will return 500
By default, SVG output is generated, suitable for inclusion. But JSON and XML
output is possible by setting `Accept:` request header
| Accept Request Header | Output format
-------------------------+---------------------
| | SVG image
| application/json | JSON data
| application/obs+xml | XML output
-----------------------------------------------
Areas of Responsibility
-----------------------
* Fetch and cache internal data from OBS and present it in usable format:
+ Generate SVG output for specific OBS project or package
+ Generate JSON/XML output for automated processing
* Low-overhead
* Monitors RabbitMQ interface for notification of OBS package and project status
* Produces SVG output based on GET request
* Cache results (sqlite) and periodically update results from OBS (in case of messages are missing)
Target Usage
------------
* inside README.md of package git or project git
* README.md of package git or project git
* comment section of a Gitea PR
* automated build result processing
Running
-------
@@ -58,4 +42,3 @@ Default parameters can be changed by env variables
| `OBS_STATUS_SERVICE_LISTEN` | [::1]:8080 | Listening address and port
| `OBS_STATUS_SERVICE_CERT` | /run/obs-status-service.pem | Location of certificate file for service
| `OBS_STATUS_SERVICE_KEY` | /run/obs-status-service.pem | Location of key file for service
| `REDIS` | | OBS's Redis instance URL

View File

@@ -275,7 +275,7 @@ func main() {
res.Write([]byte("404 page not found\n"))
})
http.HandleFunc("GET /status/{Project}", func(res http.ResponseWriter, req *http.Request) {
mime := ParseMimeHeader(req)
mime := ParseMimeHeaderAndQuery(req)
obsPrj := req.PathValue("Project")
common.LogInfo(" GET /status/"+obsPrj, "["+mime.MimeType()+"]")
@@ -296,7 +296,7 @@ func main() {
}
})
http.HandleFunc("GET /status/{Project}/{Package}", func(res http.ResponseWriter, req *http.Request) {
mime := ParseMimeHeader(req)
mime := ParseMimeHeaderAndQuery(req)
obsPrj := req.PathValue("Project")
obsPkg := req.PathValue("Package")
common.LogInfo(" GET /status/"+obsPrj+"/"+obsPkg, "["+mime.MimeType()+"]")
@@ -326,7 +326,7 @@ func main() {
})
http.HandleFunc("GET /status/{Project}/{Package}/{Repository}", func(res http.ResponseWriter, req *http.Request) {
mime := ParseMimeHeader(req)
mime := ParseMimeHeaderAndQuery(req)
obsPrj := req.PathValue("Project")
obsPkg := req.PathValue("Package")
repo := req.PathValue("Repository")
@@ -355,7 +355,7 @@ func main() {
}
})
http.HandleFunc("GET /status/{Project}/{Package}/{Repository}/{Arch}", func(res http.ResponseWriter, req *http.Request) {
mime := ParseMimeHeader(req)
mime := ParseMimeHeaderAndQuery(req)
prj := req.PathValue("Project")
pkg := req.PathValue("Package")
repo := req.PathValue("Repository")

View File

@@ -21,7 +21,16 @@ var AcceptedStatusMimes []string = []string{
XmlMime,
}
func ParseMimeHeader(req *http.Request) *MimeHeader {
func ParseMimeHeaderAndQuery(req *http.Request) *MimeHeader {
format := req.URL.Query().Get("format")
switch format {
case "svg":
return &MimeHeader{MimeHeader: SvgMime}
case "json":
return &MimeHeader{MimeHeader: JsonMime}
case "xml":
return &MimeHeader{MimeHeader: XmlMime}
}
proposedMimes := req.Header.Values("Accept")
mime := MimeHeader{MimeHeader: SvgMime}
if len(proposedMimes) == 0 {

View File

@@ -6,7 +6,6 @@ import (
"html"
"net/url"
"slices"
"strings"
)
type SvgWriter struct {
@@ -134,7 +133,7 @@ func (svg *SvgWriter) WritePackageStatus(loglink, arch, status, detail string) {
}
func (svg *SvgWriter) WriteProjectStatus(project, repo, arch, status string, count int) {
u, err := url.Parse(*ObsUrl + "/project/monitor/" + url.PathEscape(project) + "?defaults=0&amp;" + url.QueryEscape(status) + "=1&amp;arch_" + url.QueryEscape(arch) + "=1&amp;repo_" + url.QueryEscape(strings.ReplaceAll(repo, ".", "_")) + "=1")
u, err := url.Parse(*ObsUrl + "/project/monitor/" + url.PathEscape(project) + "?defaults=0&amp;" + url.QueryEscape(status) + "=1&amp;arch_" + url.QueryEscape(arch) + "=1&amp;repo_" + url.QueryEscape(repo) + "=1")
if err != nil {
return
}

View File

@@ -9,10 +9,6 @@ EnvironmentFile=-/etc/default/%i/workflow-direct.env
DynamicUser=yes
NoNewPrivileges=yes
ProtectSystem=strict
RuntimeDirectory=%i
# SLES 15 doesn't have HOME set for dynamic users, so we improvise
BindReadOnlyPaths=/etc/default/%i/known_hosts:/etc/ssh/ssh_known_hosts /etc/default/%i/config.json:%t/%i/config.json /etc/default/%i/id_ed25519 /etc/default/%i/id_ed25519.pub
WorkingDirectory=%t/%i
[Install]
WantedBy=multi-user.target

View File

@@ -10,6 +10,9 @@ Areas of responsibility
* on repository adds, creates a new submodule (if non empty)
* on repository removal, removes the submodule
NOTE: reverts (push HEAD^) are not supported as they would step-on the
work of the workflow-pr bot. Manual update of the project git is
required in this case.
Configuration
-------------
@@ -23,20 +26,6 @@ Uses `workflow.config` for configuration. Parameters
NOTE: `-rm`, `-removed`, `-deleted` are all removed suffixes used to indicate current branch is a placeholder for previously existing package. These branches will be ignored by the bot, and if default, the package will be removed and will not be added to the project.
Running
-------
* `GITEA_TOKEN` (required)
* `AMQP_USERNAME` (required)
* `AMQP_PASSWORD` (required)
* `AUTOGITS_CONFIG` (required)
* `AUTOGITS_URL` - default: https://src.opensuse.org
* `AUTOGITS_RABBITURL` - default: amqps://rabbit.opensuse.org
* `AUTOGITS_DEBUG` - disabled by default, set to any value to enable
* `AUTOGITS_CHECK_ON_START` - disabled by default, set to any value to enable
* `AUTOGITS_REPO_PATH` - default is temporary directory
* `AUTOGITS_IDENTITY_FILE` - in case where we need explicit identify path for ssh specified
Target Usage
------------

View File

@@ -104,11 +104,11 @@ func processConfiguredRepositoryAction(action *common.RepositoryWebhookEvent, co
switch action.Action {
case "created":
if action.Repository.Object_Format_Name != "sha256" {
common.LogError(" - ", action.Repository.Name, "repo is not sha256. Ignoring.")
common.LogError(" - '%s' repo is not sha256. Ignoring.", action.Repository.Name)
return
}
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name))
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all")
branch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, action.Repository.Name), "branch", "--show-current"))
if branch != configBranch {
@@ -205,13 +205,13 @@ func processConfiguredPushAction(action *common.PushWebhookEvent, config *common
return
}
git.GitExecOrPanic(gitPrj, "submodule", "update", "--init", "--depth", "1", "--checkout", action.Repository.Name)
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all")
if err := git.GitExec(filepath.Join(gitPrj, action.Repository.Name), "fetch", "--depth", "1", "--force", "origin", branch+":"+branch); err != nil {
if err := git.GitExec(filepath.Join(gitPrj, action.Repository.Name), "fetch", "--depth", "1", "--force", remoteName, branch+":"+branch); err != nil {
common.LogError("Error fetching branch:", branch, "Ignoring as non-existent.", err)
return
}
id, err := git.GitRemoteHead(filepath.Join(gitPrj, action.Repository.Name), "origin", branch)
id, err := git.GitRemoteHead(filepath.Join(gitPrj, action.Repository.Name), remoteName, branch)
common.PanicOnError(err)
if action.Head_Commit.Id == id {
git.GitExecOrPanic(filepath.Join(gitPrj, action.Repository.Name), "checkout", id)
@@ -244,7 +244,7 @@ func verifyProjectState(git common.Git, org string, config *common.AutogitConfig
remoteName, err := git.GitClone(gitPrj, gitBranch, repo.SSHURL)
common.PanicOnError(err)
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all")
common.LogDebug(" * Getting submodule list")
sub, err := git.GitSubmoduleList(gitPrj, "HEAD")
@@ -279,18 +279,15 @@ next_package:
//}
branch := config.Branch
common.LogDebug(" verifying package:", commitId, "->", filename, "@", branch)
common.LogDebug(" verifying package: %s -> %s(%s)", commitId, filename, branch)
if repo, err := gitea.GetRepository(org, filename); repo == nil && err == nil {
common.LogDebug(" repository removed...")
git.GitExecOrPanic(gitPrj, "rm", filename)
isGitUpdated = true
continue
} else if err != nil {
common.LogError("failed fetching repo data", org, filename, err)
continue
} else if len(branch) == 0 {
}
if len(branch) == 0 {
branch = repo.DefaultBranch
common.LogDebug(" -> using default branch", branch)
if common.IsRemovedBranch(branch) {
common.LogDebug(" Default branch for", filename, "is excluded")
git.GitExecOrPanic(gitPrj, "rm", filename)
@@ -504,7 +501,7 @@ func updateConfiguration(configFilename string, orgs *[]string) {
}
func main() {
configFilename := flag.String("config", "config.json", "List of PrjGit")
configFilename := flag.String("config", "", "List of PrjGit")
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance")
rabbitUrl := flag.String("url", "amqps://rabbit.opensuse.org", "URL for RabbitMQ instance")
flag.BoolVar(&DebugMode, "debug", false, "Extra debugging information")
@@ -523,29 +520,6 @@ func main() {
os.Exit(1)
}
if cf := os.Getenv("AUTOGITS_CONFIG"); len(cf) > 0 {
*configFilename = cf
}
if url := os.Getenv("AUTOGITS_URL"); len(url) > 0 {
*giteaUrl = url
}
if url := os.Getenv("AUTOGITS_RABBITURL"); len(url) > 0 {
*rabbitUrl = url
}
if debug := os.Getenv("AUTOGITS_DEBUG"); len(debug) > 0 {
DebugMode = true
}
if check := os.Getenv("AUTOGITS_CHECK_ON_START"); len(check) > 0 {
checkOnStart = true
}
if p := os.Getenv("AUTOGITS_REPO_PATH"); len(p) > 0 {
*basePath = p
}
if DebugMode {
common.SetLoggingLevel(common.LogLevelDebug)
}
defs := &common.RabbitMQGiteaEventsProcessor{}
var err error

View File

@@ -35,7 +35,6 @@ JSON
* _ReviewRequired_: (true, false) ignores that submitter is a maintainer and require a review from other maintainer IFF available
* _NoProjectGitPR_: (true, false) do not create PrjGit PRs, but still process reviews, etc.
* _Permissions_: permissions and associated accounts/groups. See below.
* _Labels_: (string, string) Labels for PRs. See below.
NOTE: `-rm`, `-removed`, `-deleted` are all removed suffixes used to indicate current branch is a placeholder for previously existing package. These branches will be ignored by the bot, and if default, the package will be removed and will not be added to the project.
example:
@@ -75,18 +74,6 @@ results in
* moo -> package and project reviews, but ignored
Labels
------
The following labels are used, when defined in Repo/Org.
| Label Config Entry | Default label | Description
|--------------------|----------------|----------------------------------------
| 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
Maintainership
--------------

View File

@@ -7,7 +7,7 @@ import "src.opensuse.org/autogits/common"
type StateChecker interface {
VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error)
CheckRepos()
CheckRepos() error
ConsistencyCheckProcess() error
}

View File

@@ -227,18 +227,12 @@ func (pr *PRProcessor) CreatePRjGitPR(prjGitPRbranch string, prset *common.PRSet
}
title, desc := PrjGitDescription(prset)
pr, err, isNew := Gitea.CreatePullRequestIfNotExist(PrjGit, prjGitPRbranch, PrjGitBranch, title, desc)
pr, err := Gitea.CreatePullRequestIfNotExist(PrjGit, prjGitPRbranch, PrjGitBranch, title, desc)
if err != nil {
common.LogError("Error creating PrjGit PR:", err)
return err
}
org := PrjGit.Owner.UserName
repo := PrjGit.Name
idx := pr.Index
if isNew {
Gitea.SetLabels(org, repo, idx, []string{prset.Config.Label(common.Label_StagingAuto)})
}
Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{
Gitea.UpdatePullRequest(PrjGit.Owner.UserName, PrjGit.Name, pr.Index, &models.EditPullRequestOption{
RemoveDeadline: true,
})
@@ -388,10 +382,6 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
prjGitPRbranch := prGitBranchNameForPR(prRepo, prNo)
prjGitPR, err := prset.GetPrjGitPR()
if err == common.PRSet_PrjGitMissing {
if req.State != "open" {
common.LogDebug("This PR is closed and no ProjectGit PR. Ignoring.")
return nil
}
common.LogDebug("Missing PrjGit. Need to create one under branch", prjGitPRbranch)
if err = pr.CreatePRjGitPR(prjGitPRbranch, prset); err != nil {

View File

@@ -103,7 +103,7 @@ func TestOpenPR(t *testing.T) {
}
// gitea.EXPECT().GetAssociatedPrjGitPR("test", "prjcopy", "test", "testRepo", int64(1)).Return(nil, nil)
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(giteaPR, nil, true)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(giteaPR, nil)
gitea.EXPECT().GetPullRequest("test", "testRepo", int64(1)).Return(giteaPR, nil)
gitea.EXPECT().RequestReviews(giteaPR, "reviewer1", "reviewer2").Return(nil, nil)
gitea.EXPECT().GetPullRequestReviews("test", "testRepo", int64(0)).Return([]*models.PullReview{}, nil)
@@ -153,7 +153,7 @@ func TestOpenPR(t *testing.T) {
}
failedErr := errors.New("Returned error here")
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr, false)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr)
err := pr.Process(event)
if err != failedErr {
@@ -193,7 +193,7 @@ func TestOpenPR(t *testing.T) {
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
gitea.EXPECT().GetPullRequest("test", "testRepo", int64(1)).Return(giteaPR, nil)
gitea.EXPECT().GetPullRequestReviews("org", "SomeRepo", int64(13)).Return([]*models.PullReview{}, nil)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(giteaPR, nil, true)
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(giteaPR, nil)
gitea.EXPECT().RequestReviews(giteaPR, "reviewer1", "reviewer2").Return(nil, failedErr)
gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())

View File

@@ -1,6 +1,7 @@
package main
import (
"errors"
"fmt"
"math/rand"
"path"
@@ -42,15 +43,6 @@ func pullRequestToEventState(state models.StateType) string {
}
func (s *DefaultStateChecker) ProcessPR(pr *models.PullRequest, config *common.AutogitConfig) error {
defer func() {
if r := recover(); r != nil {
common.LogError("panic caught in ProcessPR", common.PRtoString(pr))
if err, ok := r.(error); !ok {
common.LogError(err)
}
common.LogError(string(debug.Stack()))
}
}()
return ProcesPullRequest(pr, common.AutogitConfigs{config})
}
@@ -159,7 +151,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
return PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules)
}
func (s *DefaultStateChecker) CheckRepos() {
func (s *DefaultStateChecker) CheckRepos() error {
defer func() {
if r := recover(); r != nil {
common.LogError("panic caught")
@@ -169,6 +161,7 @@ func (s *DefaultStateChecker) CheckRepos() {
common.LogError(string(debug.Stack()))
}
}()
errorList := make([]error, 0, 10)
for org, configs := range s.processor.configuredRepos {
for _, config := range configs {
@@ -182,12 +175,12 @@ func (s *DefaultStateChecker) CheckRepos() {
prs, err := s.i.VerifyProjectState(config)
if err != nil {
common.LogError(" *** verification failed, org:", org, err)
errorList = append(errorList, err)
}
for _, pr := range prs {
prs, err := Gitea.GetRecentPullRequests(pr.Org, pr.Repo, pr.Branch)
if err != nil {
common.LogError("Error fetching pull requests for", fmt.Sprintf("%s/%s#%s", pr.Org, pr.Repo, pr.Branch), err)
break
return fmt.Errorf("Error fetching pull requests for %s/%s#%s. Err: %w", pr.Org, pr.Repo, pr.Branch, err)
}
if len(prs) > 0 {
common.LogDebug(fmt.Sprintf("%s/%s#%s", pr.Org, pr.Repo, pr.Branch), " - # of PRs to check:", len(prs))
@@ -200,11 +193,9 @@ func (s *DefaultStateChecker) CheckRepos() {
common.LogInfo(" ++ verification complete, org:", org, "config:", config.GitProjectName)
}
if len(configs) == 0 {
common.LogError(" org:", org, "has 0 configs?")
}
}
return errors.Join(errorList...)
}
func (s *DefaultStateChecker) ConsistencyCheckProcess() error {