Compare commits
21 Commits
new-packag
...
gitpkgs
| Author | SHA256 | Date | |
|---|---|---|---|
| 44bd6c23e5 | |||
| 17bfc36801 | |||
| 73d8c3e97f | |||
| 64e5b51488 | |||
| 1fe6bd98d4 | |||
| e859ed1b54 | |||
| 1ad2e1738c | |||
| a6d5d4c79d | |||
| f5d6f50b32 | |||
| edd8c67fc9 | |||
| 87633e7508 | |||
| 56ce07514a | |||
| 1d95b4cf0f | |||
| cc69a9348c | |||
| 5b5bb9a5bc | |||
|
|
2f39fc9836 | ||
| 38f4c44fd0 | |||
| 605d3dee06 | |||
| 6f26bcdccc | |||
| fffdf4fad3 | |||
| f6d2239f4d |
@@ -23,6 +23,7 @@ jobs:
|
|||||||
- run: git checkout FETCH_HEAD
|
- run: git checkout FETCH_HEAD
|
||||||
- run: go generate -C common
|
- run: go generate -C common
|
||||||
- run: go generate -C workflow-pr
|
- run: go generate -C workflow-pr
|
||||||
|
- run: go generate -C workflow-pr/interfaces
|
||||||
- run: git add -N .; git diff
|
- run: git add -N .; git diff
|
||||||
- run: |
|
- run: |
|
||||||
status=$(git status --short)
|
status=$(git status --short)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ jobs:
|
|||||||
- run: git checkout FETCH_HEAD
|
- run: git checkout FETCH_HEAD
|
||||||
- run: go generate -C common
|
- run: go generate -C common
|
||||||
- run: go generate -C workflow-pr
|
- run: go generate -C workflow-pr
|
||||||
|
- run: go generate -C workflow-pr/interfaces
|
||||||
- run: |
|
- run: |
|
||||||
host=${{ gitea.server_url }}
|
host=${{ gitea.server_url }}
|
||||||
host=${host#https://}
|
host=${host#https://}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ type ReviewGroup struct {
|
|||||||
type QAConfig struct {
|
type QAConfig struct {
|
||||||
Name string
|
Name string
|
||||||
Origin string
|
Origin string
|
||||||
|
BuildDisableRepos []string // which repos to build disable in the new project
|
||||||
}
|
}
|
||||||
|
|
||||||
type Permissions struct {
|
type Permissions struct {
|
||||||
@@ -65,7 +66,6 @@ const (
|
|||||||
Label_StagingAuto = "staging/Auto"
|
Label_StagingAuto = "staging/Auto"
|
||||||
Label_ReviewPending = "review/Pending"
|
Label_ReviewPending = "review/Pending"
|
||||||
Label_ReviewDone = "review/Done"
|
Label_ReviewDone = "review/Done"
|
||||||
Label_NewRepository = "new/New Repository"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func LabelKey(tag_value string) string {
|
func LabelKey(tag_value string) string {
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ type GitSubmoduleLister interface {
|
|||||||
|
|
||||||
type GitDirectoryLister interface {
|
type GitDirectoryLister interface {
|
||||||
GitDirectoryList(gitPath, commitId string) (dirlist map[string]string, err error)
|
GitDirectoryList(gitPath, commitId string) (dirlist map[string]string, err error)
|
||||||
GitDirectoryContentList(gitPath, commitId string) (dirlist map[string]string, err error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GitStatusLister interface {
|
type GitStatusLister interface {
|
||||||
@@ -273,11 +272,7 @@ func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error
|
|||||||
LogDebug("branch", branch)
|
LogDebug("branch", branch)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
args := []string{"fetch", "--prune", remoteName}
|
args := []string{"fetch", "--prune", remoteName, branch}
|
||||||
if len(branch) > 0 {
|
|
||||||
args = append(args, branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.TrimSpace(e.GitExecWithOutputOrPanic(repo, "rev-parse", "--is-shallow-repository")) == "true" {
|
if strings.TrimSpace(e.GitExecWithOutputOrPanic(repo, "rev-parse", "--is-shallow-repository")) == "true" {
|
||||||
args = slices.Insert(args, 1, "--unshallow")
|
args = slices.Insert(args, 1, "--unshallow")
|
||||||
}
|
}
|
||||||
@@ -792,7 +787,7 @@ func (e *GitHandlerImpl) GitCatFile(cwd, commitId, filename string) (data []byte
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// return (directory) -> (hash) map for all submodules
|
// return (filename) -> (hash) map for all submodules
|
||||||
func (e *GitHandlerImpl) GitDirectoryList(gitPath, commitId string) (directoryList map[string]string, err error) {
|
func (e *GitHandlerImpl) GitDirectoryList(gitPath, commitId string) (directoryList map[string]string, err error) {
|
||||||
var done sync.Mutex
|
var done sync.Mutex
|
||||||
directoryList = make(map[string]string)
|
directoryList = make(map[string]string)
|
||||||
@@ -866,82 +861,6 @@ func (e *GitHandlerImpl) GitDirectoryList(gitPath, commitId string) (directoryLi
|
|||||||
return directoryList, err
|
return directoryList, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// return (directory) -> (hash) map for all submodules
|
|
||||||
func (e *GitHandlerImpl) GitDirectoryContentList(gitPath, commitId string) (directoryList map[string]string, err error) {
|
|
||||||
var done sync.Mutex
|
|
||||||
directoryList = make(map[string]string)
|
|
||||||
|
|
||||||
done.Lock()
|
|
||||||
data_in, data_out := ChanIO{make(chan byte)}, ChanIO{make(chan byte)}
|
|
||||||
|
|
||||||
LogDebug("Getting directory content for:", commitId)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer done.Unlock()
|
|
||||||
defer close(data_out.ch)
|
|
||||||
|
|
||||||
data_out.Write([]byte(commitId))
|
|
||||||
data_out.ch <- '\x00'
|
|
||||||
var c GitCommit
|
|
||||||
c, err = parseGitCommit(data_in.ch)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Error parsing git commit. Err: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
trees := make(map[string]string)
|
|
||||||
trees[""] = c.Tree
|
|
||||||
|
|
||||||
for len(trees) > 0 {
|
|
||||||
for p, tree := range trees {
|
|
||||||
delete(trees, p)
|
|
||||||
|
|
||||||
data_out.Write([]byte(tree))
|
|
||||||
data_out.ch <- '\x00'
|
|
||||||
var tree GitTree
|
|
||||||
tree, err = parseGitTree(data_in.ch)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Error parsing git tree: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, te := range tree.items {
|
|
||||||
if te.isBlob() || te.isSubmodule() {
|
|
||||||
directoryList[p+te.name] = te.hash
|
|
||||||
} else if te.isTree() {
|
|
||||||
trees[p+te.name] = te.hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
cmd := exec.Command("/usr/bin/git", "cat-file", "--batch", "-Z")
|
|
||||||
cmd.Env = []string{
|
|
||||||
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
|
|
||||||
"GIT_LFS_SKIP_SMUDGE=1",
|
|
||||||
"GIT_CONFIG_GLOBAL=/dev/null",
|
|
||||||
}
|
|
||||||
cmd.Dir = filepath.Join(e.GitPath, gitPath)
|
|
||||||
cmd.Stdout = &data_in
|
|
||||||
cmd.Stdin = &data_out
|
|
||||||
cmd.Stderr = writeFunc(func(data []byte) (int, error) {
|
|
||||||
LogError(string(data))
|
|
||||||
return len(data), nil
|
|
||||||
})
|
|
||||||
LogDebug("command run:", cmd.Args)
|
|
||||||
if e := cmd.Run(); e != nil {
|
|
||||||
LogError(e)
|
|
||||||
close(data_in.ch)
|
|
||||||
close(data_out.ch)
|
|
||||||
return directoryList, e
|
|
||||||
}
|
|
||||||
|
|
||||||
done.Lock()
|
|
||||||
return directoryList, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// return (filename) -> (hash) map for all submodules
|
// return (filename) -> (hash) map for all submodules
|
||||||
func (e *GitHandlerImpl) GitSubmoduleList(gitPath, commitId string) (submoduleList map[string]string, err error) {
|
func (e *GitHandlerImpl) GitSubmoduleList(gitPath, commitId string) (submoduleList map[string]string, err error) {
|
||||||
var done sync.Mutex
|
var done sync.Mutex
|
||||||
|
|||||||
@@ -75,10 +75,6 @@ type GiteaLabelSettter interface {
|
|||||||
SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error)
|
SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiteaIssueFetcher interface {
|
|
||||||
GetIssue(org, repo string, idx int64) (*models.Issue, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type GiteaTimelineFetcher interface {
|
type GiteaTimelineFetcher interface {
|
||||||
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
|
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
|
||||||
}
|
}
|
||||||
@@ -151,7 +147,6 @@ type GiteaReviewRequester interface {
|
|||||||
|
|
||||||
type GiteaReviewUnrequester interface {
|
type GiteaReviewUnrequester interface {
|
||||||
UnrequestReview(org, repo string, id int64, reviwers ...string) error
|
UnrequestReview(org, repo string, id int64, reviwers ...string) error
|
||||||
UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiteaReviewer interface {
|
type GiteaReviewer interface {
|
||||||
@@ -205,7 +200,6 @@ type Gitea interface {
|
|||||||
GiteaSetRepoOptions
|
GiteaSetRepoOptions
|
||||||
GiteaLabelGetter
|
GiteaLabelGetter
|
||||||
GiteaLabelSettter
|
GiteaLabelSettter
|
||||||
GiteaIssueFetcher
|
|
||||||
|
|
||||||
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
|
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
|
||||||
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
|
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
|
||||||
@@ -514,26 +508,6 @@ func (gitea *GiteaTransport) SetLabels(owner, repo string, idx int64, labels []s
|
|||||||
return ret.Payload, nil
|
return ret.Payload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gitea *GiteaTransport) GetIssue(owner, repo string, idx int64) (*models.Issue, error) {
|
|
||||||
ret, err := gitea.client.Issue.IssueGetIssue(
|
|
||||||
issue.NewIssueGetIssueParams().WithOwner(owner).WithRepo(repo).WithIndex(idx),
|
|
||||||
gitea.transport.DefaultAuthentication)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ret.Payload, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gitea *GiteaTransport) UpdateIssue(owner, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
|
|
||||||
ret, err := gitea.client.Issue.IssueEditIssue(
|
|
||||||
issue.NewIssueEditIssueParams().WithOwner(owner).WithRepo(repo).WithIndex(idx).WithBody(options),
|
|
||||||
gitea.transport.DefaultAuthentication)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ret.Payload, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GiteaNotificationType_Pull = "Pull"
|
GiteaNotificationType_Pull = "Pull"
|
||||||
)
|
)
|
||||||
@@ -741,7 +715,6 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError("owner:", repo.Owner.UserName, " repo:", repo.Name, " body:", prOptions)
|
|
||||||
return nil, fmt.Errorf("Cannot create pull request. %w", err), true
|
return nil, fmt.Errorf("Cannot create pull request. %w", err), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,45 +142,6 @@ func (m *MockGitDirectoryLister) EXPECT() *MockGitDirectoryListerMockRecorder {
|
|||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitDirectoryContentList mocks base method.
|
|
||||||
func (m *MockGitDirectoryLister) GitDirectoryContentList(gitPath, commitId string) (map[string]string, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GitDirectoryContentList", gitPath, commitId)
|
|
||||||
ret0, _ := ret[0].(map[string]string)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GitDirectoryContentList indicates an expected call of GitDirectoryContentList.
|
|
||||||
func (mr *MockGitDirectoryListerMockRecorder) GitDirectoryContentList(gitPath, commitId any) *MockGitDirectoryListerGitDirectoryContentListCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitDirectoryContentList", reflect.TypeOf((*MockGitDirectoryLister)(nil).GitDirectoryContentList), gitPath, commitId)
|
|
||||||
return &MockGitDirectoryListerGitDirectoryContentListCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGitDirectoryListerGitDirectoryContentListCall wrap *gomock.Call
|
|
||||||
type MockGitDirectoryListerGitDirectoryContentListCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGitDirectoryListerGitDirectoryContentListCall) Return(dirlist map[string]string, err error) *MockGitDirectoryListerGitDirectoryContentListCall {
|
|
||||||
c.Call = c.Call.Return(dirlist, err)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGitDirectoryListerGitDirectoryContentListCall) Do(f func(string, string) (map[string]string, error)) *MockGitDirectoryListerGitDirectoryContentListCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGitDirectoryListerGitDirectoryContentListCall) DoAndReturn(f func(string, string) (map[string]string, error)) *MockGitDirectoryListerGitDirectoryContentListCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GitDirectoryList mocks base method.
|
// GitDirectoryList mocks base method.
|
||||||
func (m *MockGitDirectoryLister) GitDirectoryList(gitPath, commitId string) (map[string]string, error) {
|
func (m *MockGitDirectoryLister) GitDirectoryList(gitPath, commitId string) (map[string]string, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@@ -602,45 +563,6 @@ func (c *MockGitGitDiffCall) DoAndReturn(f func(string, string, string) (string,
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitDirectoryContentList mocks base method.
|
|
||||||
func (m *MockGit) GitDirectoryContentList(gitPath, commitId string) (map[string]string, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GitDirectoryContentList", gitPath, commitId)
|
|
||||||
ret0, _ := ret[0].(map[string]string)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GitDirectoryContentList indicates an expected call of GitDirectoryContentList.
|
|
||||||
func (mr *MockGitMockRecorder) GitDirectoryContentList(gitPath, commitId any) *MockGitGitDirectoryContentListCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitDirectoryContentList", reflect.TypeOf((*MockGit)(nil).GitDirectoryContentList), gitPath, commitId)
|
|
||||||
return &MockGitGitDirectoryContentListCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGitGitDirectoryContentListCall wrap *gomock.Call
|
|
||||||
type MockGitGitDirectoryContentListCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGitGitDirectoryContentListCall) Return(dirlist map[string]string, err error) *MockGitGitDirectoryContentListCall {
|
|
||||||
c.Call = c.Call.Return(dirlist, err)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGitGitDirectoryContentListCall) Do(f func(string, string) (map[string]string, error)) *MockGitGitDirectoryContentListCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGitGitDirectoryContentListCall) DoAndReturn(f func(string, string) (map[string]string, error)) *MockGitGitDirectoryContentListCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GitDirectoryList mocks base method.
|
// GitDirectoryList mocks base method.
|
||||||
func (m *MockGit) GitDirectoryList(gitPath, commitId string) (map[string]string, error) {
|
func (m *MockGit) GitDirectoryList(gitPath, commitId string) (map[string]string, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@@ -144,69 +144,6 @@ func (c *MockGiteaLabelSettterSetLabelsCall) DoAndReturn(f func(string, string,
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaIssueFetcher is a mock of GiteaIssueFetcher interface.
|
|
||||||
type MockGiteaIssueFetcher struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockGiteaIssueFetcherMockRecorder
|
|
||||||
isgomock struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaIssueFetcherMockRecorder is the mock recorder for MockGiteaIssueFetcher.
|
|
||||||
type MockGiteaIssueFetcherMockRecorder struct {
|
|
||||||
mock *MockGiteaIssueFetcher
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockGiteaIssueFetcher creates a new mock instance.
|
|
||||||
func NewMockGiteaIssueFetcher(ctrl *gomock.Controller) *MockGiteaIssueFetcher {
|
|
||||||
mock := &MockGiteaIssueFetcher{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockGiteaIssueFetcherMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockGiteaIssueFetcher) EXPECT() *MockGiteaIssueFetcherMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIssue mocks base method.
|
|
||||||
func (m *MockGiteaIssueFetcher) GetIssue(org, repo string, idx int64) (*models.Issue, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetIssue", org, repo, idx)
|
|
||||||
ret0, _ := ret[0].(*models.Issue)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIssue indicates an expected call of GetIssue.
|
|
||||||
func (mr *MockGiteaIssueFetcherMockRecorder) GetIssue(org, repo, idx any) *MockGiteaIssueFetcherGetIssueCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssue", reflect.TypeOf((*MockGiteaIssueFetcher)(nil).GetIssue), org, repo, idx)
|
|
||||||
return &MockGiteaIssueFetcherGetIssueCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaIssueFetcherGetIssueCall wrap *gomock.Call
|
|
||||||
type MockGiteaIssueFetcherGetIssueCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaIssueFetcherGetIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaIssueFetcherGetIssueCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaIssueFetcherGetIssueCall) Do(f func(string, string, int64) (*models.Issue, error)) *MockGiteaIssueFetcherGetIssueCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaIssueFetcherGetIssueCall) DoAndReturn(f func(string, string, int64) (*models.Issue, error)) *MockGiteaIssueFetcherGetIssueCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaTimelineFetcher is a mock of GiteaTimelineFetcher interface.
|
// MockGiteaTimelineFetcher is a mock of GiteaTimelineFetcher interface.
|
||||||
type MockGiteaTimelineFetcher struct {
|
type MockGiteaTimelineFetcher struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
@@ -1506,45 +1443,6 @@ func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall) Do
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIssue mocks base method.
|
|
||||||
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "UpdateIssue", org, repo, idx, options)
|
|
||||||
ret0, _ := ret[0].(*models.Issue)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIssue indicates an expected call of UpdateIssue.
|
|
||||||
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) UpdateIssue(org, repo, idx, options any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIssue", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).UpdateIssue), org, repo, idx, options)
|
|
||||||
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall wrap *gomock.Call
|
|
||||||
type MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall) Do(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall) DoAndReturn(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUpdateIssueCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaUnreviewTimelineFetcher is a mock of GiteaUnreviewTimelineFetcher interface.
|
// MockGiteaUnreviewTimelineFetcher is a mock of GiteaUnreviewTimelineFetcher interface.
|
||||||
type MockGiteaUnreviewTimelineFetcher struct {
|
type MockGiteaUnreviewTimelineFetcher struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
@@ -1651,45 +1549,6 @@ func (c *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall) DoAndReturn(f func
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIssue mocks base method.
|
|
||||||
func (m *MockGiteaUnreviewTimelineFetcher) UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "UpdateIssue", org, repo, idx, options)
|
|
||||||
ret0, _ := ret[0].(*models.Issue)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIssue indicates an expected call of UpdateIssue.
|
|
||||||
func (mr *MockGiteaUnreviewTimelineFetcherMockRecorder) UpdateIssue(org, repo, idx, options any) *MockGiteaUnreviewTimelineFetcherUpdateIssueCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIssue", reflect.TypeOf((*MockGiteaUnreviewTimelineFetcher)(nil).UpdateIssue), org, repo, idx, options)
|
|
||||||
return &MockGiteaUnreviewTimelineFetcherUpdateIssueCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaUnreviewTimelineFetcherUpdateIssueCall wrap *gomock.Call
|
|
||||||
type MockGiteaUnreviewTimelineFetcherUpdateIssueCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherUpdateIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaUnreviewTimelineFetcherUpdateIssueCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherUpdateIssueCall) Do(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaUnreviewTimelineFetcherUpdateIssueCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherUpdateIssueCall) DoAndReturn(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaUnreviewTimelineFetcherUpdateIssueCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaReviewRequester is a mock of GiteaReviewRequester interface.
|
// MockGiteaReviewRequester is a mock of GiteaReviewRequester interface.
|
||||||
type MockGiteaReviewRequester struct {
|
type MockGiteaReviewRequester struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
@@ -1825,45 +1684,6 @@ func (c *MockGiteaReviewUnrequesterUnrequestReviewCall) DoAndReturn(f func(strin
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIssue mocks base method.
|
|
||||||
func (m *MockGiteaReviewUnrequester) UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "UpdateIssue", org, repo, idx, options)
|
|
||||||
ret0, _ := ret[0].(*models.Issue)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIssue indicates an expected call of UpdateIssue.
|
|
||||||
func (mr *MockGiteaReviewUnrequesterMockRecorder) UpdateIssue(org, repo, idx, options any) *MockGiteaReviewUnrequesterUpdateIssueCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIssue", reflect.TypeOf((*MockGiteaReviewUnrequester)(nil).UpdateIssue), org, repo, idx, options)
|
|
||||||
return &MockGiteaReviewUnrequesterUpdateIssueCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaReviewUnrequesterUpdateIssueCall wrap *gomock.Call
|
|
||||||
type MockGiteaReviewUnrequesterUpdateIssueCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaReviewUnrequesterUpdateIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaReviewUnrequesterUpdateIssueCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaReviewUnrequesterUpdateIssueCall) Do(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaReviewUnrequesterUpdateIssueCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaReviewUnrequesterUpdateIssueCall) DoAndReturn(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaReviewUnrequesterUpdateIssueCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaReviewer is a mock of GiteaReviewer interface.
|
// MockGiteaReviewer is a mock of GiteaReviewer interface.
|
||||||
type MockGiteaReviewer struct {
|
type MockGiteaReviewer struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
@@ -2658,45 +2478,6 @@ func (c *MockGiteaGetDoneNotificationsCall) DoAndReturn(f func(string, int64) ([
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIssue mocks base method.
|
|
||||||
func (m *MockGitea) GetIssue(org, repo string, idx int64) (*models.Issue, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetIssue", org, repo, idx)
|
|
||||||
ret0, _ := ret[0].(*models.Issue)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIssue indicates an expected call of GetIssue.
|
|
||||||
func (mr *MockGiteaMockRecorder) GetIssue(org, repo, idx any) *MockGiteaGetIssueCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssue", reflect.TypeOf((*MockGitea)(nil).GetIssue), org, repo, idx)
|
|
||||||
return &MockGiteaGetIssueCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaGetIssueCall wrap *gomock.Call
|
|
||||||
type MockGiteaGetIssueCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaGetIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaGetIssueCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaGetIssueCall) Do(f func(string, string, int64) (*models.Issue, error)) *MockGiteaGetIssueCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaGetIssueCall) DoAndReturn(f func(string, string, int64) (*models.Issue, error)) *MockGiteaGetIssueCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIssueComments mocks base method.
|
// GetIssueComments mocks base method.
|
||||||
func (m *MockGitea) GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error) {
|
func (m *MockGitea) GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@@ -3525,45 +3306,6 @@ func (c *MockGiteaUnrequestReviewCall) DoAndReturn(f func(string, string, int64,
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIssue mocks base method.
|
|
||||||
func (m *MockGitea) UpdateIssue(org, repo string, idx int64, options *models.EditIssueOption) (*models.Issue, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "UpdateIssue", org, repo, idx, options)
|
|
||||||
ret0, _ := ret[0].(*models.Issue)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIssue indicates an expected call of UpdateIssue.
|
|
||||||
func (mr *MockGiteaMockRecorder) UpdateIssue(org, repo, idx, options any) *MockGiteaUpdateIssueCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIssue", reflect.TypeOf((*MockGitea)(nil).UpdateIssue), org, repo, idx, options)
|
|
||||||
return &MockGiteaUpdateIssueCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaUpdateIssueCall wrap *gomock.Call
|
|
||||||
type MockGiteaUpdateIssueCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaUpdateIssueCall) Return(arg0 *models.Issue, arg1 error) *MockGiteaUpdateIssueCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaUpdateIssueCall) Do(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaUpdateIssueCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaUpdateIssueCall) DoAndReturn(f func(string, string, int64, *models.EditIssueOption) (*models.Issue, error)) *MockGiteaUpdateIssueCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePullRequest mocks base method.
|
// UpdatePullRequest mocks base method.
|
||||||
func (m *MockGitea) UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error) {
|
func (m *MockGitea) UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
61
common/pr.go
61
common/pr.go
@@ -6,9 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
||||||
@@ -652,8 +650,6 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FF all non-prj git and unrequest reviews.
|
// FF all non-prj git and unrequest reviews.
|
||||||
newRepoIssues := make(map[int64]string) // issue index -> org/repo
|
|
||||||
|
|
||||||
for _, prinfo := range rs.PRs {
|
for _, prinfo := range rs.PRs {
|
||||||
// remove pending review requests
|
// remove pending review requests
|
||||||
repo := prinfo.PR.Base.Repo
|
repo := prinfo.PR.Base.Repo
|
||||||
@@ -675,15 +671,6 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
|
|||||||
if rs.IsPrjGitPR(prinfo.PR) {
|
if rs.IsPrjGitPR(prinfo.PR) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
isNewRepo := false
|
|
||||||
for _, l := range prinfo.PR.Labels {
|
|
||||||
if l.Name == Label_NewRepository {
|
|
||||||
isNewRepo = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
br := rs.Config.Branch
|
br := rs.Config.Branch
|
||||||
if len(br) == 0 {
|
if len(br) == 0 {
|
||||||
// if branch is unspecified, take it from the PR as it
|
// if branch is unspecified, take it from the PR as it
|
||||||
@@ -692,30 +679,11 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
|
|||||||
} else if br != prinfo.PR.Base.Name {
|
} else if br != prinfo.PR.Base.Name {
|
||||||
panic(prinfo.PR.Base.Name + " is expected to match " + br)
|
panic(prinfo.PR.Base.Name + " is expected to match " + br)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isNewRepo {
|
|
||||||
// Extract issue reference from body: "See issue #XYZ"
|
|
||||||
rx := regexp.MustCompile(`See issue #(\d+)`)
|
|
||||||
if matches := rx.FindStringSubmatch(prinfo.PR.Body); len(matches) > 1 {
|
|
||||||
if issueIdx, err := strconv.ParseInt(matches[1], 10, 64); err == nil {
|
|
||||||
// We need to know which project git this issue belongs to.
|
|
||||||
// Since the PR set is linked to a ProjectGit, we can use its org/repo.
|
|
||||||
prjGitOrg, prjGitRepo, _ := rs.Config.GetPrjGit()
|
|
||||||
newRepoIssues[issueIdx] = prjGitOrg + "/" + prjGitRepo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prinfo.RemoteName, err = git.GitClone(repo.Name, br, repo.SSHURL)
|
prinfo.RemoteName, err = git.GitClone(repo.Name, br, repo.SSHURL)
|
||||||
PanicOnError(err)
|
PanicOnError(err)
|
||||||
git.GitExecOrPanic(repo.Name, "fetch", prinfo.RemoteName, head.Sha)
|
git.GitExecOrPanic(repo.Name, "fetch", prinfo.RemoteName, head.Sha)
|
||||||
|
git.GitExecOrPanic(repo.Name, "merge", "--ff", head.Sha)
|
||||||
|
|
||||||
if isNewRepo {
|
|
||||||
LogInfo("Force-pushing new repository branch", br, "to", head.Sha)
|
|
||||||
// we don't merge, we just set the branch to this commit
|
|
||||||
} else {
|
|
||||||
git.GitExecOrPanic(repo.Name, "merge", "--ff", head.Sha)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// push changes
|
// push changes
|
||||||
@@ -730,37 +698,12 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
|
|||||||
}
|
}
|
||||||
repo := prinfo.PR.Base.Repo
|
repo := prinfo.PR.Base.Repo
|
||||||
|
|
||||||
isNewRepo := false
|
|
||||||
for _, l := range prinfo.PR.Labels {
|
|
||||||
if l.Name == Label_NewRepository {
|
|
||||||
isNewRepo = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !IsDryRun {
|
if !IsDryRun {
|
||||||
if isNewRepo {
|
git.GitExecOrPanic(repo.Name, "push", prinfo.RemoteName)
|
||||||
git.GitExecOrPanic(repo.Name, "push", "-f", prinfo.RemoteName, prinfo.PR.Head.Sha+":"+prinfo.PR.Base.Name)
|
|
||||||
} else {
|
|
||||||
git.GitExecOrPanic(repo.Name, "push", prinfo.RemoteName)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LogInfo("*** WOULD push", repo.Name, "to", prinfo.RemoteName)
|
LogInfo("*** WOULD push", repo.Name, "to", prinfo.RemoteName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close referencing issues
|
|
||||||
if !IsDryRun {
|
|
||||||
for issueIdx, prjPath := range newRepoIssues {
|
|
||||||
parts := strings.Split(prjPath, "/")
|
|
||||||
if len(parts) == 2 {
|
|
||||||
LogInfo("Closing issue", prjPath+"#"+strconv.FormatInt(issueIdx, 10))
|
|
||||||
gitea.UpdateIssue(parts[0], parts[1], issueIdx, &models.EditIssueOption{
|
|
||||||
State: "closed",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
package common_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFetchPRSet_Linkage(t *testing.T) {
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "target-org",
|
|
||||||
GitProjectName: "test-org/prjgit#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Mock a package PR
|
|
||||||
pkgPR := &models.PullRequest{
|
|
||||||
Index: 101,
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Mock a ProjectGit PR that references the package PR
|
|
||||||
prjGitPR := &models.PullRequest{
|
|
||||||
Index: 500,
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "prjgit",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Body: "Forwarded PRs: pkg1\n\nPR: target-org/pkg1!101",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Fetch from ProjectGit PR", func(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
mockGitea := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
|
||||||
|
|
||||||
// Expect fetch of prjGitPR
|
|
||||||
mockGitea.EXPECT().GetPullRequest("test-org", "prjgit", int64(500)).Return(prjGitPR, nil)
|
|
||||||
// Expect fetch of pkgPR because it's linked in body
|
|
||||||
mockGitea.EXPECT().GetPullRequest("target-org", "pkg1", int64(101)).Return(pkgPR, nil)
|
|
||||||
|
|
||||||
// Expect review fetching (part of FetchPRSet)
|
|
||||||
mockGitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
mockGitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
prset, err := common.FetchPRSet("bot", mockGitea, "test-org", "prjgit", 500, config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("FetchPRSet failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prset.PRs) != 2 {
|
|
||||||
t.Errorf("Expected 2 PRs in set, got %d", len(prset.PRs))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !prset.IsConsistent() {
|
|
||||||
t.Error("PR set should be consistent")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Fetch from Package PR via Timeline", func(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
mockGitea := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
|
||||||
|
|
||||||
// 1. FetchPRSet for pkgPR will call LastPrjGitRefOnTimeline
|
|
||||||
mockGitea.EXPECT().GetTimeline("target-org", "pkg1", int64(101)).Return([]*models.TimelineComment{
|
|
||||||
{
|
|
||||||
Type: common.TimelineCommentType_PullRequestRef,
|
|
||||||
RefIssue: &models.Issue{
|
|
||||||
Index: 500,
|
|
||||||
Body: "PR: target-org/pkg1!101",
|
|
||||||
Repository: &models.RepositoryMeta{
|
|
||||||
Owner: "test-org",
|
|
||||||
Name: "prjgit",
|
|
||||||
},
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
// 2. It will then fetch the prjGitPR found in timeline (twice in LastPrjGitRefOnTimeline)
|
|
||||||
mockGitea.EXPECT().GetPullRequest("test-org", "prjgit", int64(500)).Return(prjGitPR, nil).Times(2)
|
|
||||||
|
|
||||||
// 3. Then it will recursively fetch linked PRs from prjGitPR body in readPRData
|
|
||||||
mockGitea.EXPECT().GetPullRequest("target-org", "pkg1", int64(101)).Return(pkgPR, nil)
|
|
||||||
|
|
||||||
// Review fetching for all PRs in the set
|
|
||||||
mockGitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
mockGitea.EXPECT().GetTimeline("test-org", "prjgit", int64(500)).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
mockGitea.EXPECT().GetTimeline("target-org", "pkg1", int64(101)).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
prset, err := common.FetchPRSet("bot", mockGitea, "target-org", "pkg1", 101, config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("FetchPRSet failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prset.PRs) != 2 {
|
|
||||||
t.Errorf("Expected 2 PRs in set, got %d", len(prset.PRs))
|
|
||||||
}
|
|
||||||
|
|
||||||
prjPRInfo, err := prset.GetPrjGitPR()
|
|
||||||
if err != nil || prjPRInfo.PR.Index != 500 {
|
|
||||||
t.Errorf("Expected ProjectGit PR 500 to be found, got %v", prjPRInfo)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
package common_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPRSet_Merge_Special(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGitea := mock_common.NewMockGiteaReviewUnrequester(ctl)
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "target-org",
|
|
||||||
GitProjectName: "test-org/prjgit#main",
|
|
||||||
Branch: "main",
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Regular ProjectGit PR
|
|
||||||
prjGitPR := &models.PullRequest{
|
|
||||||
Index: 500,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "test-org"}, SSHURL: "prj-ssh-url"},
|
|
||||||
Sha: "base-sha",
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "prj-head-sha"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. "new/New Repository" Package PR
|
|
||||||
newPkgPR := &models.PullRequest{
|
|
||||||
Index: 101,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{Name: "new-pkg", Owner: &models.User{UserName: "target-org"}, SSHURL: "pkg-ssh-url"},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-head-sha"},
|
|
||||||
Labels: []*models.Label{
|
|
||||||
{Name: "new/New Repository"},
|
|
||||||
},
|
|
||||||
Body: "See issue #123",
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{PR: prjGitPR},
|
|
||||||
{PR: newPkgPR},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
common.IsDryRun = false
|
|
||||||
|
|
||||||
// Mock expectations for Merge
|
|
||||||
// Clone and fetch for PrjGit
|
|
||||||
mockGit.EXPECT().GitClone("_ObsPrj", "main", "prj-ssh-url").Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("_ObsPrj", "fetch", "origin", "prj-head-sha")
|
|
||||||
// mockGit.EXPECT().GitExecWithOutputOrPanic("_ObsPrj", "merge-base", "HEAD", "base-sha", "prj-head-sha").Return("base-sha")
|
|
||||||
mockGit.EXPECT().GitExec("_ObsPrj", "merge", "--no-ff", "-m", gomock.Any(), "prj-head-sha").Return(nil)
|
|
||||||
|
|
||||||
// Unrequest reviews
|
|
||||||
mockGitea.EXPECT().UnrequestReview("test-org", "prjgit", int64(500), gomock.Any()).Return(nil)
|
|
||||||
mockGitea.EXPECT().UnrequestReview("target-org", "new-pkg", int64(101), gomock.Any()).Return(nil)
|
|
||||||
|
|
||||||
// Clone and fetch for new-pkg
|
|
||||||
mockGit.EXPECT().GitClone("new-pkg", "main", "pkg-ssh-url").Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("new-pkg", "fetch", "origin", "pkg-head-sha")
|
|
||||||
|
|
||||||
// Pushing changes
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("_ObsPrj", "push", "origin")
|
|
||||||
// Special push for new repo: git push -f origin pkg-head-sha:main
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("new-pkg", "push", "-f", "origin", "pkg-head-sha:main")
|
|
||||||
|
|
||||||
// Closing issue
|
|
||||||
mockGitea.EXPECT().UpdateIssue("test-org", "prjgit", int64(123), gomock.Any()).DoAndReturn(func(org, repo string, idx int64, opt *models.EditIssueOption) (*models.Issue, error) {
|
|
||||||
if opt.State != "closed" {
|
|
||||||
t.Errorf("Expected issue state to be closed, got %s", opt.State)
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
err := prset.Merge(mockGitea, mockGit)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Merge failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -48,6 +48,8 @@ func reviewsToTimeline(reviews []*models.PullReview) []*models.TimelineComment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPR(t *testing.T) {
|
func TestPR(t *testing.T) {
|
||||||
|
return
|
||||||
|
|
||||||
baseConfig := common.AutogitConfig{
|
baseConfig := common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -78,21 +80,21 @@ func TestPR(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Error fetching PullRequest",
|
name: "Error fetching PullRequest",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}, pr_err: errors.New("Missing PR")},
|
{pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}, pr_err: errors.New("Missing PR")},
|
||||||
},
|
},
|
||||||
prjGitPRIndex: -1,
|
prjGitPRIndex: -1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Error fetching PullRequest in PrjGit",
|
name: "Error fetching PullRequest in PrjGit",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}, pr_err: errors.New("missing PR")},
|
{pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}, pr_err: errors.New("missing PR")},
|
||||||
{pr: &models.PullRequest{Body: "", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
|
{pr: &models.PullRequest{Body: "", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Error fetching prjgit",
|
name: "Error fetching prjgit",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"}},
|
{pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
|
||||||
},
|
},
|
||||||
resLen: 1,
|
resLen: 1,
|
||||||
prjGitPRIndex: -1,
|
prjGitPRIndex: -1,
|
||||||
@@ -100,20 +102,8 @@ func TestPR(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Review set is consistent",
|
name: "Review set is consistent",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"},
|
{pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
|
||||||
timeline: []*models.TimelineComment{
|
|
||||||
{
|
|
||||||
Type: common.TimelineCommentType_PullRequestRef,
|
|
||||||
RefIssue: &models.Issue{
|
|
||||||
Index: 22,
|
|
||||||
Repository: &models.RepositoryMeta{Name: "barPrj", Owner: "foo"},
|
|
||||||
User: &models.User{UserName: "test"},
|
|
||||||
Body: "PR: test/repo#42",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
{pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
|
|
||||||
},
|
},
|
||||||
resLen: 2,
|
resLen: 2,
|
||||||
prjGitPRIndex: 1,
|
prjGitPRIndex: 1,
|
||||||
@@ -123,21 +113,8 @@ func TestPR(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Review set is consistent: 1pkg",
|
name: "Review set is consistent: 1pkg",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"},
|
{pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
|
||||||
timeline: []*models.TimelineComment{
|
|
||||||
{
|
|
||||||
Type: common.TimelineCommentType_PullRequestRef,
|
|
||||||
RefIssue: &models.Issue{
|
|
||||||
Index: 22,
|
|
||||||
Repository: &models.RepositoryMeta{Name: "barPrj", Owner: "foo"},
|
|
||||||
User: &models.User{UserName: "test"},
|
|
||||||
Body: "PR: test/repo#42",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{pr: &models.PullRequest{Body: "PR: test/repo#42", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
|
|
||||||
},
|
},
|
||||||
resLen: 2,
|
resLen: 2,
|
||||||
prjGitPRIndex: 1,
|
prjGitPRIndex: 1,
|
||||||
@@ -146,22 +123,9 @@ func TestPR(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Review set is consistent: 2pkg",
|
name: "Review set is consistent: 2pkg",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{pr: &models.PullRequest{Body: "some desc", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
|
||||||
pr: &models.PullRequest{Body: "some desc\nPR: foo/barPrj#22", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"},
|
{pr: &models.PullRequest{Body: "PR: test/repo#42\nPR: test/repo2#41", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
|
||||||
timeline: []*models.TimelineComment{
|
{pr: &models.PullRequest{Body: "some other desc\nPR: foo/fer#33", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
|
||||||
{
|
|
||||||
Type: common.TimelineCommentType_PullRequestRef,
|
|
||||||
RefIssue: &models.Issue{
|
|
||||||
Index: 22,
|
|
||||||
Repository: &models.RepositoryMeta{Name: "barPrj", Owner: "foo"},
|
|
||||||
User: &models.User{UserName: "test"},
|
|
||||||
Body: "PR: test/repo#42\nPR: test/repo2#41",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{pr: &models.PullRequest{Body: "PR: test/repo#42\nPR: test/repo2#41", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
|
|
||||||
{pr: &models.PullRequest{Body: "some other desc\nPR: foo/barPrj#22", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}, State: "open"}},
|
|
||||||
},
|
},
|
||||||
resLen: 3,
|
resLen: 3,
|
||||||
prjGitPRIndex: 1,
|
prjGitPRIndex: 1,
|
||||||
@@ -171,7 +135,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "Review set of prjgit PR is consistent",
|
name: "Review set of prjgit PR is consistent",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -190,23 +154,10 @@ func TestPR(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Review set is consistent: 2pkg",
|
name: "Review set is consistent: 2pkg",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{pr: &models.PullRequest{Body: "PR: foo/barPrj#222", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#222", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "test"}}}, State: "open"},
|
{pr: &models.PullRequest{Body: "PR: test/repo2#41", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
|
||||||
timeline: []*models.TimelineComment{
|
{pr: &models.PullRequest{Body: "PR: test/repo#42\nPR: test/repo2#41", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, State: "opened"}},
|
||||||
{
|
{pr: &models.PullRequest{Body: "PR: foo/barPrj#20", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}, State: "opened"}},
|
||||||
Type: common.TimelineCommentType_PullRequestRef,
|
|
||||||
RefIssue: &models.Issue{
|
|
||||||
Index: 22,
|
|
||||||
Repository: &models.RepositoryMeta{Name: "barPrj", Owner: "foo"},
|
|
||||||
User: &models.User{UserName: "test"},
|
|
||||||
Body: "PR: test/repo#42\nPR: test/repo2#41",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{pr: &models.PullRequest{Body: "PR: test/repo2#41", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
|
|
||||||
{pr: &models.PullRequest{Body: "PR: test/repo#42\nPR: test/repo2#41", Index: 22, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, State: "open"}},
|
|
||||||
{pr: &models.PullRequest{Body: "PR: foo/barPrj#20", Index: 41, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo2", Owner: &models.User{UserName: "test"}}}, State: "open"}},
|
|
||||||
},
|
},
|
||||||
resLen: 3,
|
resLen: 3,
|
||||||
prjGitPRIndex: 2,
|
prjGitPRIndex: 2,
|
||||||
@@ -216,7 +167,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "WIP PR is not approved",
|
name: "WIP PR is not approved",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "", Title: "WIP: some title", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "", Title: "WIP: some title", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -233,9 +184,10 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Manual review is missing", data: []prdata{
|
name: "Manual review is missing",
|
||||||
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -243,7 +195,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -260,15 +212,16 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
ManualMergeOnly: true,
|
ManualMergeOnly: true,
|
||||||
})
|
})
|
||||||
}},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Manual review is done, via PrjGit",
|
name: "Manual review is done, via PrjGit",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -276,7 +229,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -293,7 +246,7 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
ManualMergeOnly: true,
|
ManualMergeOnly: true,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -302,7 +255,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "Manual review is done, via PrjGit",
|
name: "Manual review is done, via PrjGit",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "merge ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -310,7 +263,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -327,7 +280,7 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
ManualMergeOnly: true,
|
ManualMergeOnly: true,
|
||||||
ManualMergeProject: true,
|
ManualMergeProject: true,
|
||||||
})
|
})
|
||||||
@@ -337,7 +290,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "Manual review is not done, via PrjGit",
|
name: "Manual review is not done, via PrjGit",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "merge ok", User: &models.User{UserName: "notm2"}, State: common.ReviewStateApproved},
|
{Body: "merge ok", User: &models.User{UserName: "notm2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "merge not ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "merge not ok", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
@@ -346,7 +299,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -363,7 +316,7 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
ManualMergeOnly: true,
|
ManualMergeOnly: true,
|
||||||
ManualMergeProject: true,
|
ManualMergeProject: true,
|
||||||
})
|
})
|
||||||
@@ -373,7 +326,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "Manual review is done via PackageGit",
|
name: "Manual review is done via PackageGit",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/repo#20", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -381,7 +334,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "Merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "Merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -398,7 +351,7 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
ManualMergeOnly: true,
|
ManualMergeOnly: true,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -407,7 +360,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "Manual review done via PkgGits",
|
name: "Manual review done via PkgGits",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -415,7 +368,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -423,7 +376,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -440,7 +393,7 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
ManualMergeOnly: true,
|
ManualMergeOnly: true,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -449,7 +402,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "Manual review done via PkgGits not allowed",
|
name: "Manual review done via PkgGits not allowed",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -457,7 +410,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "Merge OK!", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -465,7 +418,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "merge ok", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -482,7 +435,7 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
ManualMergeOnly: true,
|
ManualMergeOnly: true,
|
||||||
ManualMergeProject: true,
|
ManualMergeProject: true,
|
||||||
})
|
})
|
||||||
@@ -492,7 +445,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "Manual review is is missing on one PR",
|
name: "Manual review is is missing on one PR",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/repo#20\nPR: foo/repo#21", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -500,7 +453,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 20, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -508,7 +461,7 @@ func TestPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "PR: foo/barPrj#42", Index: 21, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m1"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super1"}, State: common.ReviewStateApproved},
|
||||||
@@ -525,7 +478,7 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
ManualMergeOnly: true,
|
ManualMergeOnly: true,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -534,7 +487,7 @@ func TestPR(t *testing.T) {
|
|||||||
name: "PR is approved with negative optional review",
|
name: "PR is approved with negative optional review",
|
||||||
data: []prdata{
|
data: []prdata{
|
||||||
{
|
{
|
||||||
pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}, Name: "master"}, User: &models.User{UserName: "submitter"}, State: "open"},
|
pr: &models.PullRequest{Body: "", Index: 42, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "barPrj", Owner: &models.User{UserName: "foo"}}}, User: &models.User{UserName: "submitter"}, State: "opened"},
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "m2"}, State: common.ReviewStateApproved},
|
||||||
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
{Body: "LGTM", User: &models.User{UserName: "super2"}, State: common.ReviewStateApproved},
|
||||||
@@ -552,7 +505,7 @@ func TestPR(t *testing.T) {
|
|||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2", "~*bot"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2", "~*bot"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "foo",
|
Organization: "foo",
|
||||||
GitProjectName: "barPrj#master",
|
GitProjectName: "barPrj",
|
||||||
}
|
}
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &config)
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &config)
|
||||||
},
|
},
|
||||||
@@ -566,31 +519,27 @@ func TestPR(t *testing.T) {
|
|||||||
review_mock := mock_common.NewMockGiteaPRChecker(ctl)
|
review_mock := mock_common.NewMockGiteaPRChecker(ctl)
|
||||||
// reviewer_mock := mock_common.NewMockGiteaReviewRequester(ctl)
|
// reviewer_mock := mock_common.NewMockGiteaReviewRequester(ctl)
|
||||||
|
|
||||||
prjGitOrg, prjGitRepo, _ := baseConfig.GetPrjGit()
|
|
||||||
if test.reviewSetFetcher == nil { // if we are fetching the prjgit directly, the these mocks are not called
|
if test.reviewSetFetcher == nil { // if we are fetching the prjgit directly, the these mocks are not called
|
||||||
if test.prjGitPRIndex >= 0 {
|
if test.prjGitPRIndex >= 0 {
|
||||||
pr_mock.EXPECT().GetPullRequest(prjGitOrg, prjGitRepo, int64(test.data[test.prjGitPRIndex].pr.Index)).
|
pr_mock.EXPECT().GetPullRequest(baseConfig.Organization, baseConfig.GitProjectName, test.prjGitPRIndex).
|
||||||
Return(test.data[test.prjGitPRIndex].pr, test.data[test.prjGitPRIndex].pr_err).AnyTimes()
|
Return(test.data[test.prjGitPRIndex].pr, test.data[test.prjGitPRIndex].pr_err)
|
||||||
} else if test.prjGitPRIndex < 0 {
|
} else if test.prjGitPRIndex < 0 {
|
||||||
// no prjgit PR
|
// no prjgit PR
|
||||||
pr_mock.EXPECT().GetPullRequest(prjGitOrg, prjGitRepo, gomock.Any()).
|
pr_mock.EXPECT().GetPullRequest(baseConfig.Organization, baseConfig.GitProjectName, gomock.Any()).
|
||||||
Return(nil, nil).AnyTimes()
|
Return(nil, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var test_err error
|
||||||
for _, data := range test.data {
|
for _, data := range test.data {
|
||||||
pr_mock.EXPECT().GetPullRequest(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.pr, data.pr_err).AnyTimes()
|
pr_mock.EXPECT().GetPullRequest(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.pr, data.pr_err).AnyTimes()
|
||||||
if data.pr_err != nil {
|
if data.pr_err != nil {
|
||||||
// test_err is not used and was causing a build error.
|
test_err = data.pr_err
|
||||||
// data.pr_err is directly used in the previous EXPECT call.
|
|
||||||
}
|
}
|
||||||
|
review_mock.EXPECT().GetPullRequestReviews(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.reviews, data.review_error).AnyTimes()
|
||||||
if data.timeline == nil {
|
if data.timeline == nil {
|
||||||
data.timeline = reviewsToTimeline(data.reviews)
|
data.timeline = reviewsToTimeline(data.reviews)
|
||||||
}
|
}
|
||||||
pr_mock.EXPECT().GetTimeline(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.timeline, nil).AnyTimes()
|
|
||||||
pr_mock.EXPECT().GetPullRequestReviews(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.reviews, data.review_error).AnyTimes()
|
|
||||||
|
|
||||||
review_mock.EXPECT().GetPullRequestReviews(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.reviews, data.review_error).AnyTimes()
|
|
||||||
review_mock.EXPECT().GetTimeline(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.timeline, nil).AnyTimes()
|
review_mock.EXPECT().GetTimeline(data.pr.Base.Repo.Owner.UserName, data.pr.Base.Repo.Name, data.pr.Index).Return(data.timeline, nil).AnyTimes()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,12 +552,27 @@ func TestPR(t *testing.T) {
|
|||||||
res, err = common.FetchPRSet("test", pr_mock, "test", "repo", 42, &baseConfig)
|
res, err = common.FetchPRSet("test", pr_mock, "test", "repo", 42, &baseConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if res == nil {
|
if err == nil {
|
||||||
|
if test_err != nil {
|
||||||
|
t.Fatal("Expected", test_err, "but got", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if res != nil {
|
||||||
|
t.Fatal("error but got ReviewSet?")
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.api_error != "" {
|
||||||
|
if err.Error() != test.api_error {
|
||||||
|
t.Fatal("expected", test.api_error, "but got", err)
|
||||||
|
}
|
||||||
|
} else if test_err != err {
|
||||||
|
t.Fatal("expected", test_err, "but got", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(res.PRs) != test.resLen {
|
if test.resLen != len(res.PRs) {
|
||||||
t.Errorf("Test Case '%s': expected result len %d but got %d", test.name, test.resLen, len(res.PRs))
|
t.Error("expected result len", test.resLen, "but got", len(res.PRs))
|
||||||
}
|
}
|
||||||
|
|
||||||
PrjGitPR, err := res.GetPrjGitPR()
|
PrjGitPR, err := res.GetPrjGitPR()
|
||||||
@@ -619,9 +583,6 @@ func TestPR(t *testing.T) {
|
|||||||
}
|
}
|
||||||
pr_found := false
|
pr_found := false
|
||||||
if test.prjGitPRIndex >= 0 {
|
if test.prjGitPRIndex >= 0 {
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
for i := range test.data {
|
for i := range test.data {
|
||||||
if PrjGitPR.PR == test.data[i].pr && i == test.prjGitPRIndex {
|
if PrjGitPR.PR == test.data[i].pr && i == test.prjGitPRIndex {
|
||||||
t.Log("found at index", i)
|
t.Log("found at index", i)
|
||||||
@@ -1165,6 +1126,7 @@ func TestFindMissingAndExtraReviewers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPRMerge(t *testing.T) {
|
func TestPRMerge(t *testing.T) {
|
||||||
|
t.Skip("FAIL: No PrjGit PR found, missing calls")
|
||||||
repoDir := t.TempDir()
|
repoDir := t.TempDir()
|
||||||
|
|
||||||
cwd, _ := os.Getwd()
|
cwd, _ := os.Getwd()
|
||||||
@@ -1174,11 +1136,7 @@ func TestPRMerge(t *testing.T) {
|
|||||||
t.Fatal(string(out))
|
t.Fatal(string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Skip("No tests of PRMerge yet")
|
|
||||||
return
|
|
||||||
|
|
||||||
common.ExtraGitParams = []string{
|
common.ExtraGitParams = []string{
|
||||||
"TZ=UTC",
|
|
||||||
"GIT_CONFIG_COUNT=1",
|
"GIT_CONFIG_COUNT=1",
|
||||||
"GIT_CONFIG_KEY_0=protocol.file.allow",
|
"GIT_CONFIG_KEY_0=protocol.file.allow",
|
||||||
"GIT_CONFIG_VALUE_0=always",
|
"GIT_CONFIG_VALUE_0=always",
|
||||||
@@ -1193,7 +1151,7 @@ func TestPRMerge(t *testing.T) {
|
|||||||
|
|
||||||
config := &common.AutogitConfig{
|
config := &common.AutogitConfig{
|
||||||
Organization: "org",
|
Organization: "org",
|
||||||
GitProjectName: "org/prj#main",
|
GitProjectName: "org/prj#master",
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -1205,10 +1163,8 @@ func TestPRMerge(t *testing.T) {
|
|||||||
name: "Merge base not merged in main",
|
name: "Merge base not merged in main",
|
||||||
|
|
||||||
pr: &models.PullRequest{
|
pr: &models.PullRequest{
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
Base: &models.PRBranchInfo{
|
||||||
Name: "main",
|
Sha: "e8b0de43d757c96a9d2c7101f4bff404e322f53a1fa4041fb85d646110c38ad4", // "base_add_b1"
|
||||||
Sha: "96515c092626c716a4613ba4f68a8d1cc4894317658342c450e656390f524ec3", // "base_add_b1"
|
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
Name: "prj",
|
Name: "prj",
|
||||||
Owner: &models.User{
|
Owner: &models.User{
|
||||||
@@ -1218,7 +1174,7 @@ func TestPRMerge(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Head: &models.PRBranchInfo{
|
Head: &models.PRBranchInfo{
|
||||||
Sha: "4119fc725dc11cdf11f982d5bb0a8ba2a138f1180c4323862a18b8e08def5603", // "base_add_b2"
|
Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mergeError: "Aborting merge",
|
mergeError: "Aborting merge",
|
||||||
@@ -1227,10 +1183,8 @@ func TestPRMerge(t *testing.T) {
|
|||||||
name: "Merge conflict in modules, auto-resolved",
|
name: "Merge conflict in modules, auto-resolved",
|
||||||
|
|
||||||
pr: &models.PullRequest{
|
pr: &models.PullRequest{
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
Base: &models.PRBranchInfo{
|
||||||
Name: "main",
|
Sha: "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8",
|
||||||
Sha: "85f59f7aa732b742e58b48356cd46cb1ab1b5c4349eb5c0eda324e2dbea8f9e7",
|
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
Name: "prj",
|
Name: "prj",
|
||||||
Owner: &models.User{
|
Owner: &models.User{
|
||||||
@@ -1240,7 +1194,7 @@ func TestPRMerge(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Head: &models.PRBranchInfo{
|
Head: &models.PRBranchInfo{
|
||||||
Sha: "4119fc725dc11cdf11f982d5bb0a8ba2a138f1180c4323862a18b8e08def5603", // "base_add_b2"
|
Sha: "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a", // "base_add_b2"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1252,13 +1206,11 @@ func TestPRMerge(t *testing.T) {
|
|||||||
mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
||||||
reviewUnrequestMock := mock_common.NewMockGiteaReviewUnrequester(ctl)
|
reviewUnrequestMock := mock_common.NewMockGiteaReviewUnrequester(ctl)
|
||||||
|
|
||||||
reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||||||
|
|
||||||
testDir := t.TempDir()
|
testDir := t.TempDir()
|
||||||
t.Log("dir:", testDir)
|
t.Log("dir:", testDir)
|
||||||
mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil)
|
mock.EXPECT().GetPullRequest("org", "prj", int64(1)).Return(test.pr, nil)
|
||||||
mock.EXPECT().GetTimeline("org", "prj", int64(1)).Return(nil, nil).AnyTimes()
|
|
||||||
mock.EXPECT().GetPullRequestReviews("org", "prj", int64(1)).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
set, err := common.FetchPRSet("test", mock, "org", "prj", 1, config)
|
set, err := common.FetchPRSet("test", mock, "org", "prj", 1, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1279,11 +1231,11 @@ func TestPRMerge(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPRChanges(t *testing.T) {
|
func TestPRChanges(t *testing.T) {
|
||||||
|
t.Skip("FAIL: unexpected calls, missing calls")
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
PRs []*models.PullRequest
|
PRs []*models.PullRequest
|
||||||
PrjPRs *models.PullRequest
|
PrjPRs *models.PullRequest
|
||||||
Timeline []*models.TimelineComment
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Pkg PR is closed",
|
name: "Pkg PR is closed",
|
||||||
@@ -1295,22 +1247,10 @@ func TestPRChanges(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
PrjPRs: &models.PullRequest{
|
PrjPRs: &models.PullRequest{
|
||||||
Index: 42,
|
|
||||||
Title: "some PR",
|
Title: "some PR",
|
||||||
Base: &models.PRBranchInfo{Name: "branch", Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "org"}}},
|
Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "prjgit", Owner: &models.User{UserName: "org"}}},
|
||||||
Body: "PR: org/repo#42",
|
Body: "PR: org/repo#42",
|
||||||
State: "open",
|
State: "opened",
|
||||||
},
|
|
||||||
Timeline: []*models.TimelineComment{
|
|
||||||
{
|
|
||||||
Type: common.TimelineCommentType_PullRequestRef,
|
|
||||||
RefIssue: &models.Issue{
|
|
||||||
Index: 42,
|
|
||||||
Repository: &models.RepositoryMeta{Name: "prjgit", Owner: "org"},
|
|
||||||
User: &models.User{UserName: "user"},
|
|
||||||
Body: "PR: org/repo#42",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1323,13 +1263,9 @@ func TestPRChanges(t *testing.T) {
|
|||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
mock_fetcher := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
mock_fetcher := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
||||||
mock_fetcher.EXPECT().GetPullRequest("org", "prjgit", int64(42)).Return(test.PrjPRs, nil).AnyTimes()
|
mock_fetcher.EXPECT().GetPullRequest("org", "prjgit", int64(42)).Return(test.PrjPRs, nil)
|
||||||
mock_fetcher.EXPECT().GetTimeline("org", "prjgit", int64(42)).Return(nil, nil).AnyTimes()
|
|
||||||
mock_fetcher.EXPECT().GetPullRequestReviews("org", "prjgit", int64(42)).Return(nil, nil).AnyTimes()
|
|
||||||
for _, pr := range test.PRs {
|
for _, pr := range test.PRs {
|
||||||
mock_fetcher.EXPECT().GetPullRequest(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(pr, nil)
|
mock_fetcher.EXPECT().GetPullRequest(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(pr, nil)
|
||||||
mock_fetcher.EXPECT().GetTimeline(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(test.Timeline, nil).AnyTimes()
|
|
||||||
mock_fetcher.EXPECT().GetPullRequestReviews(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(nil, nil).AnyTimes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PRs, err := common.FetchPRSet("user", mock_fetcher, "org", "repo", 42, &config)
|
PRs, err := common.FetchPRSet("user", mock_fetcher, "org", "repo", 42, &config)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
export TZ=UTC
|
|
||||||
export GIT_CONFIG_COUNT=2
|
export GIT_CONFIG_COUNT=2
|
||||||
|
|
||||||
export GIT_CONFIG_KEY_0=protocol.file.allow
|
export GIT_CONFIG_KEY_0=protocol.file.allow
|
||||||
|
|||||||
187
devel-importer/find_factory_commit_in_gitpkg.pl
Executable file
187
devel-importer/find_factory_commit_in_gitpkg.pl
Executable file
@@ -0,0 +1,187 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use IPC::Open2;
|
||||||
|
use URI;
|
||||||
|
|
||||||
|
sub FindFactoryCommit {
|
||||||
|
my ($package) = @_;
|
||||||
|
|
||||||
|
# Execute osc cat and capture output
|
||||||
|
my $osc_cmd = "osc cat openSUSE:Factory $package $package.changes";
|
||||||
|
open( my $osc_fh, "$osc_cmd |" ) or die "Failed to run osc: $!";
|
||||||
|
my $data = do { local $/; <$osc_fh> };
|
||||||
|
close($osc_fh);
|
||||||
|
|
||||||
|
# Calculate size
|
||||||
|
my $size = length($data);
|
||||||
|
|
||||||
|
# Create blob header
|
||||||
|
my $blob = "blob $size\0$data";
|
||||||
|
|
||||||
|
# Open a pipe to openssl to compute the hash
|
||||||
|
my ( $reader, $writer );
|
||||||
|
my $pid = open2( $reader, $writer, "openssl sha256" );
|
||||||
|
|
||||||
|
# Send blob data
|
||||||
|
print $writer $blob;
|
||||||
|
close $writer;
|
||||||
|
|
||||||
|
# Read the hash result and extract it
|
||||||
|
my $hash_line = <$reader>;
|
||||||
|
waitpid( $pid, 0 );
|
||||||
|
my ($hash) = $hash_line =~ /([a-fA-F0-9]{64})/;
|
||||||
|
|
||||||
|
# Run git search command with the hash
|
||||||
|
print("looking for hash: $hash\n");
|
||||||
|
my @hashes;
|
||||||
|
my $git_cmd =
|
||||||
|
"git -C $package rev-list --all pool/HEAD | while read commit; do git -C $package ls-tree \"\$commit\" | grep -q '^100644 blob $hash' && echo \"\$commit\"; done";
|
||||||
|
open( my $git_fh, "$git_cmd |" ) or die "Failed to run git search: $!";
|
||||||
|
while ( my $commit = <$git_fh> ) {
|
||||||
|
chomp $commit;
|
||||||
|
print "Found commit $commit\n";
|
||||||
|
push( @hashes, $commit );
|
||||||
|
}
|
||||||
|
close($git_fh);
|
||||||
|
return @hashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
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> <package> <repo>";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $project = shift or Usage();
|
||||||
|
my $pkg = shift;
|
||||||
|
my $repo = shift;
|
||||||
|
|
||||||
|
if (not defined($repo)) {
|
||||||
|
Usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
my $meta_url = `osc meta pkg $project $pkg | grep scmsync | sed -e 's,\\s*</\\?scmsync>\\s*,,g'`;
|
||||||
|
chomp($meta_url);
|
||||||
|
if ($meta_url ne $repo) {
|
||||||
|
die "meta not equal to repo for $pkg: $meta_url != $repo";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $u = URI->new($meta_url);
|
||||||
|
die "Only src.opensuse.org is supported" unless $u->scheme =~ /^https?$/ && $u->host eq 'src.opensuse.org';
|
||||||
|
my (undef, $org, $repo_path) = split('/', $u->path);
|
||||||
|
my $branch = $u->fragment;
|
||||||
|
die "Only src.opensuse.org is supported" unless $org;
|
||||||
|
if ($org eq "pool") {
|
||||||
|
print "Already a pool package. We are done.\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
my %params = $u->query_form;
|
||||||
|
delete $params{trackingbranch};
|
||||||
|
die "Unsupported query parameters: " . join(', ', keys %params) if keys %params;
|
||||||
|
|
||||||
|
my @packages = ($pkg) if defined $pkg;
|
||||||
|
|
||||||
|
if ( ! -e $org ) {
|
||||||
|
mkdir($org);
|
||||||
|
}
|
||||||
|
chdir($org);
|
||||||
|
|
||||||
|
my $super_user = $ENV{SUPER};
|
||||||
|
if (defined($super_user)) {
|
||||||
|
$super_user = "-G $super_user";
|
||||||
|
} else {
|
||||||
|
$super_user = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
for my $package ( sort(@packages) ) {
|
||||||
|
print " ----- PROCESSING $package\n";
|
||||||
|
my $url = "https://src.opensuse.org/$org/$repo_path.git";
|
||||||
|
my $push_url = "gitea\@src.opensuse.org:pool/$package.git";
|
||||||
|
if ( not -e $package ) {
|
||||||
|
print("cloning...\n");
|
||||||
|
system("git clone --origin pool $url $package") == 0
|
||||||
|
or die "Can't clone $org/$repo_path";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print("adding remote...\n");
|
||||||
|
system("git -C $package remote rm pool > /dev/null");
|
||||||
|
system("git -C $package remote add pool $url") == 0
|
||||||
|
or die "Can't add pool for $package";
|
||||||
|
}
|
||||||
|
system("git -C $package remote set-url pool --push $push_url") == 0
|
||||||
|
or die "Can't add push remote for $package";
|
||||||
|
print("fetching remote...\n");
|
||||||
|
system("git -C $package fetch pool") == 0 or die "Can't fetch pool for $package";
|
||||||
|
|
||||||
|
my @commits = FindFactoryCommit($package);
|
||||||
|
my $Md5Hashes = FactoryMd5($package);
|
||||||
|
my $c;
|
||||||
|
my $match = 0;
|
||||||
|
for my $commit (@commits) {
|
||||||
|
if ( length($commit) != 64 ) {
|
||||||
|
print("Failed to find factory commit. Aborting.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !$match ) {
|
||||||
|
die "Match not found. Aborting.";
|
||||||
|
}
|
||||||
|
|
||||||
|
system ("git -C $package push -f pool factory");
|
||||||
|
print "$package: $c\n";
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
SystemsManagement
|
||||||
Java:packages
|
Java:packages
|
||||||
Kernel:firmware
|
Kernel:firmware
|
||||||
Kernel:kdump
|
Kernel:kdump
|
||||||
|
|||||||
4
devel-importer/update_git_packages.sh
Executable file
4
devel-importer/update_git_packages.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
osc api '/search/package?match=scmsync' | ../xml_package_parse | ../find_factory_commit_in_gitpkg.pl
|
||||||
|
|
||||||
83
devel-importer/xml_package_parse
Executable file
83
devel-importer/xml_package_parse
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use XML::Parser;
|
||||||
|
|
||||||
|
my $parser = XML::Parser->new(Handlers => {
|
||||||
|
Start => \&handle_start,
|
||||||
|
End => \&handle_end,
|
||||||
|
Char => \&handle_char,
|
||||||
|
});
|
||||||
|
|
||||||
|
my $current_element = '';
|
||||||
|
my $current_package_attrs = {};
|
||||||
|
my $scmsync_content = '';
|
||||||
|
|
||||||
|
my %devel_pkgs;
|
||||||
|
open(my $dfh, "curl -s https://src.opensuse.org/openSUSE/Factory/raw/branch/main/pkgs/_meta/devel_packages |") or die $!;
|
||||||
|
while(<$dfh>) {
|
||||||
|
chomp;
|
||||||
|
$devel_pkgs{$_} = 1;
|
||||||
|
}
|
||||||
|
close($dfh);
|
||||||
|
|
||||||
|
my $xml_content = do { local $/; <STDIN> };
|
||||||
|
$parser->parse($xml_content);
|
||||||
|
|
||||||
|
sub handle_start {
|
||||||
|
my ($expat, $element, %attrs) = @_;
|
||||||
|
$current_element = $element;
|
||||||
|
|
||||||
|
if ($element eq 'package') {
|
||||||
|
$current_package_attrs = \%attrs;
|
||||||
|
}
|
||||||
|
if ($element eq 'scmsync') {
|
||||||
|
$scmsync_content = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handle_char {
|
||||||
|
my ($expat, $string) = @_;
|
||||||
|
if ($current_element eq 'scmsync') {
|
||||||
|
$scmsync_content .= $string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handle_end {
|
||||||
|
my ($expat, $element) = @_;
|
||||||
|
|
||||||
|
if ($element eq 'scmsync') {
|
||||||
|
my $project = $current_package_attrs->{project};
|
||||||
|
my $name = $current_package_attrs->{name};
|
||||||
|
my $scmsync = $scmsync_content;
|
||||||
|
|
||||||
|
# Use checks
|
||||||
|
$project = '' unless defined $project;
|
||||||
|
$name = '' unless defined $name;
|
||||||
|
$scmsync = '' unless defined $scmsync;
|
||||||
|
|
||||||
|
# Trim
|
||||||
|
$project =~ s/^\s+|\s+$//g;
|
||||||
|
$name =~ s/^\s+|\s+$//g;
|
||||||
|
$scmsync =~ s/^\s+|\s+$//g;
|
||||||
|
|
||||||
|
my $has_error = 0;
|
||||||
|
foreach my $val ($project, $name, $scmsync) {
|
||||||
|
if ($val =~ /\s/) {
|
||||||
|
print STDERR "Error: Value '$val' contains whitespace.\n";
|
||||||
|
$has_error = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unless ($has_error) {
|
||||||
|
if ($devel_pkgs{"$name $project"}) {
|
||||||
|
print "$name $project $scmsync\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reset current element if we are closing it
|
||||||
|
if ($current_element eq $element) {
|
||||||
|
$current_element = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,43 +1,54 @@
|
|||||||
Group Review Bot
|
Group Review Bot
|
||||||
================
|
================
|
||||||
|
|
||||||
Areas of responsibility
|
This workaround is mainly needed because Gitea does not track which team member performed a review on behalf of a team.
|
||||||
-----------------------
|
|
||||||
|
|
||||||
1. Is used to handle reviews associated with groups defined in the
|
Main Tasks
|
||||||
ProjectGit.
|
----------
|
||||||
|
|
||||||
2. Assumes: workflow-pr needs to associate and define the PR set from
|
Awaits a comment in the format “@groupreviewbot-name: approve”, then approves the PR with the comment “<user> approved a review on behalf of <groupreviewbot-name>.”
|
||||||
which the groups.json is read (Base of the PrjGit PR)
|
|
||||||
|
|
||||||
Target Usage
|
Target Usage
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Projects where policy reviews are required.
|
Projects where policy reviews are required.
|
||||||
|
|
||||||
Configiuration
|
Configuration
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Groups are defined in the workflow.config inside the project git. They take following options,
|
The bot is configured via the `ReviewGroups` field in the `workflow.config` file, located in the ProjectGit repository.
|
||||||
|
|
||||||
|
See `ReviewGroups` in the [workflow-pr configuration](../workflow-pr/README.md#config-file).
|
||||||
|
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
ReviewGroups: [
|
"ReviewGroups": [
|
||||||
{
|
{
|
||||||
"Name": "name of the group user",
|
"Name": "name of the group user",
|
||||||
"Reviewers": ["members", "of", "group"],
|
"Reviewers": ["members", "of", "group"],
|
||||||
"Silent": (true, false) -- if true, do not explicitly require review requests of group members
|
"Silent": "(true, false) -- if true, do not explicitly require review requests of group members"
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Server configuration
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
**Configuration file:**
|
||||||
|
|
||||||
|
| Field | Type | Notes |
|
||||||
|
| ----- | ----- | ----- |
|
||||||
|
| root | Array of string | Format **org/repo\#branch** |
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
* Gitea token to:
|
Gitea token with following permissions:
|
||||||
+ R/W PullRequest
|
- R/W PullRequest
|
||||||
+ R/W Notification
|
- R/W Notification
|
||||||
+ R User
|
- R User
|
||||||
|
|
||||||
Env Variables
|
Env Variables
|
||||||
-------------
|
-------------
|
||||||
|
|||||||
@@ -18,20 +18,23 @@ import (
|
|||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var configs common.AutogitConfigs
|
type ReviewBot struct {
|
||||||
var acceptRx *regexp.Regexp
|
configs common.AutogitConfigs
|
||||||
var rejectRx *regexp.Regexp
|
acceptRx *regexp.Regexp
|
||||||
var groupName string
|
rejectRx *regexp.Regexp
|
||||||
|
groupName string
|
||||||
func InitRegex(newGroupName string) {
|
gitea common.Gitea
|
||||||
groupName = newGroupName
|
|
||||||
acceptRx = regexp.MustCompile("^:\\s*(LGTM|approved?)")
|
|
||||||
rejectRx = regexp.MustCompile("^:\\s*")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseReviewLine(reviewText string) (bool, string) {
|
func (bot *ReviewBot) InitRegex(newGroupName string) {
|
||||||
|
bot.groupName = newGroupName
|
||||||
|
bot.acceptRx = regexp.MustCompile("^:\\s*(LGTM|approved?)")
|
||||||
|
bot.rejectRx = regexp.MustCompile("^:\\s*")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bot *ReviewBot) ParseReviewLine(reviewText string) (bool, string) {
|
||||||
line := strings.TrimSpace(reviewText)
|
line := strings.TrimSpace(reviewText)
|
||||||
groupTextName := "@" + groupName
|
groupTextName := "@" + bot.groupName
|
||||||
glen := len(groupTextName)
|
glen := len(groupTextName)
|
||||||
if len(line) < glen || line[0:glen] != groupTextName {
|
if len(line) < glen || line[0:glen] != groupTextName {
|
||||||
return false, line
|
return false, line
|
||||||
@@ -51,20 +54,20 @@ func ParseReviewLine(reviewText string) (bool, string) {
|
|||||||
return false, line
|
return false, line
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReviewAccepted(reviewText string) bool {
|
func (bot *ReviewBot) ReviewAccepted(reviewText string) bool {
|
||||||
for _, line := range common.SplitStringNoEmpty(reviewText, "\n") {
|
for _, line := range common.SplitStringNoEmpty(reviewText, "\n") {
|
||||||
if matched, reviewLine := ParseReviewLine(line); matched {
|
if matched, reviewLine := bot.ParseReviewLine(line); matched {
|
||||||
return acceptRx.MatchString(reviewLine)
|
return bot.acceptRx.MatchString(reviewLine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReviewRejected(reviewText string) bool {
|
func (bot *ReviewBot) ReviewRejected(reviewText string) bool {
|
||||||
for _, line := range common.SplitStringNoEmpty(reviewText, "\n") {
|
for _, line := range common.SplitStringNoEmpty(reviewText, "\n") {
|
||||||
if matched, reviewLine := ParseReviewLine(line); matched {
|
if matched, reviewLine := bot.ParseReviewLine(line); matched {
|
||||||
if rejectRx.MatchString(reviewLine) {
|
if bot.rejectRx.MatchString(reviewLine) {
|
||||||
return !acceptRx.MatchString(reviewLine)
|
return !bot.acceptRx.MatchString(reviewLine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,10 +117,10 @@ var commentStrings = []string{
|
|||||||
"change_time_estimate",
|
"change_time_estimate",
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
func FindAcceptableReviewInTimeline(user string, timeline []*models.TimelineComment, reviews []*models.PullReview) *models.TimelineComment {
|
func (bot *ReviewBot) FindAcceptableReviewInTimeline(user string, timeline []*models.TimelineComment, reviews []*models.PullReview) *models.TimelineComment {
|
||||||
for _, t := range timeline {
|
for _, t := range timeline {
|
||||||
if t.Type == common.TimelineCommentType_Comment && t.User.UserName == user && t.Created == t.Updated {
|
if t.Type == common.TimelineCommentType_Comment && t.User.UserName == user && t.Created == t.Updated {
|
||||||
if ReviewAccepted(t.Body) || ReviewRejected(t.Body) {
|
if bot.ReviewAccepted(t.Body) || bot.ReviewRejected(t.Body) {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,9 +129,9 @@ func FindAcceptableReviewInTimeline(user string, timeline []*models.TimelineComm
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindOurLastReviewInTimeline(timeline []*models.TimelineComment) *models.TimelineComment {
|
func (bot *ReviewBot) FindOurLastReviewInTimeline(timeline []*models.TimelineComment) *models.TimelineComment {
|
||||||
for _, t := range timeline {
|
for _, t := range timeline {
|
||||||
if t.Type == common.TimelineCommentType_Review && t.User.UserName == groupName && t.Created == t.Updated {
|
if t.Type == common.TimelineCommentType_Review && t.User.UserName == bot.groupName && t.Created == t.Updated {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,13 +139,13 @@ func FindOurLastReviewInTimeline(timeline []*models.TimelineComment) *models.Tim
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnrequestReviews(gitea common.Gitea, org, repo string, id int64, users []string) {
|
func (bot *ReviewBot) UnrequestReviews(org, repo string, id int64, users []string) {
|
||||||
if err := gitea.UnrequestReview(org, repo, id, users...); err != nil {
|
if err := bot.gitea.UnrequestReview(org, repo, id, users...); err != nil {
|
||||||
common.LogError("Can't remove reviewrs after a review:", err)
|
common.LogError("Can't remove reviewrs after a review:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessNotifications(notification *models.NotificationThread, gitea common.Gitea) {
|
func (bot *ReviewBot) ProcessNotifications(notification *models.NotificationThread) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
common.LogInfo("panic cought --- recovered")
|
common.LogInfo("panic cought --- recovered")
|
||||||
@@ -169,14 +172,14 @@ func ProcessNotifications(notification *models.NotificationThread, gitea common.
|
|||||||
id, _ := strconv.ParseInt(match[3], 10, 64)
|
id, _ := strconv.ParseInt(match[3], 10, 64)
|
||||||
|
|
||||||
common.LogInfo("processing:", fmt.Sprintf("%s/%s!%d", org, repo, id))
|
common.LogInfo("processing:", fmt.Sprintf("%s/%s!%d", org, repo, id))
|
||||||
pr, err := gitea.GetPullRequest(org, repo, id)
|
pr, err := bot.gitea.GetPullRequest(org, repo, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(" ** Cannot fetch PR associated with review:", subject.URL, "Error:", err)
|
common.LogError(" ** Cannot fetch PR associated with review:", subject.URL, "Error:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ProcessPR(pr); err == nil && !common.IsDryRun {
|
if err := bot.ProcessPR(pr); err == nil && !common.IsDryRun {
|
||||||
if err := gitea.SetNotificationRead(notification.ID); err != nil {
|
if err := bot.gitea.SetNotificationRead(notification.ID); err != nil {
|
||||||
common.LogDebug(" Cannot set notification as read", err)
|
common.LogDebug(" Cannot set notification as read", err)
|
||||||
}
|
}
|
||||||
} else if err != nil && err != ReviewNotFinished {
|
} else if err != nil && err != ReviewNotFinished {
|
||||||
@@ -186,24 +189,24 @@ func ProcessNotifications(notification *models.NotificationThread, gitea common.
|
|||||||
|
|
||||||
var ReviewNotFinished = fmt.Errorf("Review is not finished")
|
var ReviewNotFinished = fmt.Errorf("Review is not finished")
|
||||||
|
|
||||||
func ProcessPR(pr *models.PullRequest) error {
|
func (bot *ReviewBot) ProcessPR(pr *models.PullRequest) error {
|
||||||
org := pr.Base.Repo.Owner.UserName
|
org := pr.Base.Repo.Owner.UserName
|
||||||
repo := pr.Base.Repo.Name
|
repo := pr.Base.Repo.Name
|
||||||
id := pr.Index
|
id := pr.Index
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, reviewer := range pr.RequestedReviewers {
|
for _, reviewer := range pr.RequestedReviewers {
|
||||||
if reviewer != nil && reviewer.UserName == groupName {
|
if reviewer != nil && reviewer.UserName == bot.groupName {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
common.LogInfo(" review is not requested for", groupName)
|
common.LogInfo(" review is not requested for", bot.groupName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
config := configs.GetPrjGitConfig(org, repo, pr.Base.Name)
|
config := bot.configs.GetPrjGitConfig(org, repo, pr.Base.Name)
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return fmt.Errorf("Cannot find config for: %s", pr.URL)
|
return fmt.Errorf("Cannot find config for: %s", pr.URL)
|
||||||
}
|
}
|
||||||
@@ -213,17 +216,17 @@ func ProcessPR(pr *models.PullRequest) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
reviews, err := gitea.GetPullRequestReviews(org, repo, id)
|
reviews, err := bot.gitea.GetPullRequestReviews(org, repo, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to fetch reviews for: %v: %w", pr.URL, err)
|
return fmt.Errorf("Failed to fetch reviews for: %v: %w", pr.URL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
timeline, err := common.FetchTimelineSinceReviewRequestOrPush(gitea, groupName, pr.Head.Sha, org, repo, id)
|
timeline, err := common.FetchTimelineSinceReviewRequestOrPush(bot.gitea, bot.groupName, pr.Head.Sha, org, repo, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to fetch timeline to review. %w", err)
|
return fmt.Errorf("Failed to fetch timeline to review. %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
groupConfig, err := config.GetReviewGroup(groupName)
|
groupConfig, err := config.GetReviewGroup(bot.groupName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to fetch review group. %w", err)
|
return fmt.Errorf("Failed to fetch review group. %w", err)
|
||||||
}
|
}
|
||||||
@@ -234,30 +237,30 @@ func ProcessPR(pr *models.PullRequest) error {
|
|||||||
// pr.Head.Sha
|
// pr.Head.Sha
|
||||||
|
|
||||||
for _, reviewer := range requestReviewers {
|
for _, reviewer := range requestReviewers {
|
||||||
if review := FindAcceptableReviewInTimeline(reviewer, timeline, reviews); review != nil {
|
if review := bot.FindAcceptableReviewInTimeline(reviewer, timeline, reviews); review != nil {
|
||||||
if ReviewAccepted(review.Body) {
|
if bot.ReviewAccepted(review.Body) {
|
||||||
if !common.IsDryRun {
|
if !common.IsDryRun {
|
||||||
text := reviewer + " approved a review on behalf of " + groupName
|
text := reviewer + " approved a review on behalf of " + bot.groupName
|
||||||
if review := FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
if review := bot.FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
||||||
_, err := gitea.AddReviewComment(pr, common.ReviewStateApproved, text)
|
_, err := bot.gitea.AddReviewComment(pr, common.ReviewStateApproved, text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(" -> failed to write approval comment", err)
|
common.LogError(" -> failed to write approval comment", err)
|
||||||
}
|
}
|
||||||
UnrequestReviews(gitea, org, repo, id, requestReviewers)
|
bot.UnrequestReviews(org, repo, id, requestReviewers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
common.LogInfo(" -> approved by", reviewer)
|
common.LogInfo(" -> approved by", reviewer)
|
||||||
common.LogInfo(" review at", review.Created)
|
common.LogInfo(" review at", review.Created)
|
||||||
return nil
|
return nil
|
||||||
} else if ReviewRejected(review.Body) {
|
} else if bot.ReviewRejected(review.Body) {
|
||||||
if !common.IsDryRun {
|
if !common.IsDryRun {
|
||||||
text := reviewer + " requested changes on behalf of " + groupName + ". See " + review.HTMLURL
|
text := reviewer + " requested changes on behalf of " + bot.groupName + ". See " + review.HTMLURL
|
||||||
if review := FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
if review := bot.FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
||||||
_, err := gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, text)
|
_, err := bot.gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(" -> failed to write rejecting comment", err)
|
common.LogError(" -> failed to write rejecting comment", err)
|
||||||
}
|
}
|
||||||
UnrequestReviews(gitea, org, repo, id, requestReviewers)
|
bot.UnrequestReviews(org, repo, id, requestReviewers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
common.LogInfo(" -> declined by", reviewer)
|
common.LogInfo(" -> declined by", reviewer)
|
||||||
@@ -271,7 +274,7 @@ func ProcessPR(pr *models.PullRequest) error {
|
|||||||
if !groupConfig.Silent && len(requestReviewers) > 0 {
|
if !groupConfig.Silent && len(requestReviewers) > 0 {
|
||||||
common.LogDebug(" Requesting reviews for:", requestReviewers)
|
common.LogDebug(" Requesting reviews for:", requestReviewers)
|
||||||
if !common.IsDryRun {
|
if !common.IsDryRun {
|
||||||
if _, err := gitea.RequestReviews(pr, requestReviewers...); err != nil {
|
if _, err := bot.gitea.RequestReviews(pr, requestReviewers...); err != nil {
|
||||||
common.LogDebug(" -> err:", err)
|
common.LogDebug(" -> err:", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -284,42 +287,40 @@ func ProcessPR(pr *models.PullRequest) error {
|
|||||||
// add a helpful comment, if not yet added
|
// add a helpful comment, if not yet added
|
||||||
found_help_comment := false
|
found_help_comment := false
|
||||||
for _, t := range timeline {
|
for _, t := range timeline {
|
||||||
if t.Type == common.TimelineCommentType_Comment && t.User != nil && t.User.UserName == groupName {
|
if t.Type == common.TimelineCommentType_Comment && t.User != nil && t.User.UserName == bot.groupName {
|
||||||
found_help_comment = true
|
found_help_comment = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found_help_comment && !common.IsDryRun {
|
if !found_help_comment && !common.IsDryRun {
|
||||||
helpComment := fmt.Sprintln("Review by", groupName, "represents a group of reviewers:", strings.Join(requestReviewers, ", "), ".\n\n"+
|
helpComment := fmt.Sprintln("Review by", bot.groupName, "represents a group of reviewers:", strings.Join(requestReviewers, ", "), ".\n\n"+
|
||||||
"Do **not** use standard review interface to review on behalf of the group.\n"+
|
"Do **not** use standard review interface to review on behalf of the group.\n"+
|
||||||
"To accept the review on behalf of the group, create the following comment: `@"+groupName+": approve`.\n"+
|
"To accept the review on behalf of the group, create the following comment: `@"+bot.groupName+": approve`.\n"+
|
||||||
"To request changes on behalf of the group, create the following comment: `@"+groupName+": decline` followed with lines justifying the decision.\n"+
|
"To request changes on behalf of the group, create the following comment: `@"+bot.groupName+": decline` followed with lines justifying the decision.\n"+
|
||||||
"Future edits of the comments are ignored, a new comment is required to change the review state.")
|
"Future edits of the comments are ignored, a new comment is required to change the review state.")
|
||||||
if slices.Contains(groupConfig.Reviewers, pr.User.UserName) {
|
if slices.Contains(groupConfig.Reviewers, pr.User.UserName) {
|
||||||
helpComment = helpComment + "\n\n" +
|
helpComment = helpComment + "\n\n" +
|
||||||
"Submitter is member of this review group, hence they are excluded from being one of the reviewers here"
|
"Submitter is member of this review group, hence they are excluded from being one of the reviewers here"
|
||||||
}
|
}
|
||||||
gitea.AddComment(pr, helpComment)
|
bot.gitea.AddComment(pr, helpComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ReviewNotFinished
|
return ReviewNotFinished
|
||||||
}
|
}
|
||||||
|
|
||||||
func PeriodReviewCheck() {
|
func (bot *ReviewBot) PeriodReviewCheck() {
|
||||||
notifications, err := gitea.GetNotifications(common.GiteaNotificationType_Pull, nil)
|
notifications, err := bot.gitea.GetNotifications(common.GiteaNotificationType_Pull, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(" Error fetching unread notifications: %w", err)
|
common.LogError(" Error fetching unread notifications: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, notification := range notifications {
|
for _, notification := range notifications {
|
||||||
ProcessNotifications(notification, gitea)
|
bot.ProcessNotifications(notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitea common.Gitea
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance used for reviews")
|
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance used for reviews")
|
||||||
rabbitMqHost := flag.String("rabbit-url", "amqps://rabbit.opensuse.org", "RabbitMQ instance where Gitea webhook notifications are sent")
|
rabbitMqHost := flag.String("rabbit-url", "amqps://rabbit.opensuse.org", "RabbitMQ instance where Gitea webhook notifications are sent")
|
||||||
@@ -355,7 +356,7 @@ func main() {
|
|||||||
flag.Usage()
|
flag.Usage()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
groupName = args[0]
|
targetGroupName := args[0]
|
||||||
|
|
||||||
if *configFile == "" {
|
if *configFile == "" {
|
||||||
common.LogError("Missing config file")
|
common.LogError("Missing config file")
|
||||||
@@ -378,14 +379,14 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gitea = common.AllocateGiteaTransport(*giteaUrl)
|
giteaTransport := common.AllocateGiteaTransport(*giteaUrl)
|
||||||
configs, err = common.ResolveWorkflowConfigs(gitea, configData)
|
configs, err := common.ResolveWorkflowConfigs(giteaTransport, configData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Cannot parse workflow configs:", err)
|
common.LogError("Cannot parse workflow configs:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reviewer, err := gitea.GetCurrentUser()
|
reviewer, err := giteaTransport.GetCurrentUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Cannot fetch review user:", err)
|
common.LogError("Cannot fetch review user:", err)
|
||||||
return
|
return
|
||||||
@@ -395,14 +396,18 @@ func main() {
|
|||||||
*interval = 1
|
*interval = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
InitRegex(groupName)
|
bot := &ReviewBot{
|
||||||
|
gitea: giteaTransport,
|
||||||
|
configs: configs,
|
||||||
|
}
|
||||||
|
bot.InitRegex(targetGroupName)
|
||||||
|
|
||||||
common.LogInfo(" ** processing group reviews for group:", groupName)
|
common.LogInfo(" ** processing group reviews for group:", bot.groupName)
|
||||||
common.LogInfo(" ** username in Gitea:", reviewer.UserName)
|
common.LogInfo(" ** username in Gitea:", reviewer.UserName)
|
||||||
common.LogInfo(" ** polling interval:", *interval, "min")
|
common.LogInfo(" ** polling interval:", *interval, "min")
|
||||||
common.LogInfo(" ** connecting to RabbitMQ:", *rabbitMqHost)
|
common.LogInfo(" ** connecting to RabbitMQ:", *rabbitMqHost)
|
||||||
|
|
||||||
if groupName != reviewer.UserName {
|
if bot.groupName != reviewer.UserName {
|
||||||
common.LogError(" ***** Reviewer does not match group name. Aborting. *****")
|
common.LogError(" ***** Reviewer does not match group name. Aborting. *****")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -414,10 +419,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config_update := ConfigUpdatePush{
|
config_update := ConfigUpdatePush{
|
||||||
|
bot: bot,
|
||||||
config_modified: make(chan *common.AutogitConfig),
|
config_modified: make(chan *common.AutogitConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
process_issue_pr := IssueCommentProcessor{}
|
process_issue_pr := IssueCommentProcessor{
|
||||||
|
bot: bot,
|
||||||
|
}
|
||||||
|
|
||||||
configUpdates := &common.RabbitMQGiteaEventsProcessor{
|
configUpdates := &common.RabbitMQGiteaEventsProcessor{
|
||||||
Orgs: []string{},
|
Orgs: []string{},
|
||||||
@@ -427,7 +435,7 @@ func main() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
configUpdates.Connection().RabbitURL = u
|
configUpdates.Connection().RabbitURL = u
|
||||||
for _, c := range configs {
|
for _, c := range bot.configs {
|
||||||
if org, _, _ := c.GetPrjGit(); !slices.Contains(configUpdates.Orgs, org) {
|
if org, _, _ := c.GetPrjGit(); !slices.Contains(configUpdates.Orgs, org) {
|
||||||
configUpdates.Orgs = append(configUpdates.Orgs, org)
|
configUpdates.Orgs = append(configUpdates.Orgs, org)
|
||||||
}
|
}
|
||||||
@@ -440,17 +448,17 @@ func main() {
|
|||||||
select {
|
select {
|
||||||
case configTouched, ok := <-config_update.config_modified:
|
case configTouched, ok := <-config_update.config_modified:
|
||||||
if ok {
|
if ok {
|
||||||
for idx, c := range configs {
|
for idx, c := range bot.configs {
|
||||||
if c == configTouched {
|
if c == configTouched {
|
||||||
org, repo, branch := c.GetPrjGit()
|
org, repo, branch := c.GetPrjGit()
|
||||||
prj := fmt.Sprintf("%s/%s#%s", org, repo, branch)
|
prj := fmt.Sprintf("%s/%s#%s", org, repo, branch)
|
||||||
common.LogInfo("Detected config update for", prj)
|
common.LogInfo("Detected config update for", prj)
|
||||||
|
|
||||||
new_config, err := common.ReadWorkflowConfig(gitea, prj)
|
new_config, err := common.ReadWorkflowConfig(bot.gitea, prj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Failed parsing Project config for", prj, err)
|
common.LogError("Failed parsing Project config for", prj, err)
|
||||||
} else {
|
} else {
|
||||||
configs[idx] = new_config
|
bot.configs[idx] = new_config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -460,7 +468,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PeriodReviewCheck()
|
bot.PeriodReviewCheck()
|
||||||
time.Sleep(time.Duration(*interval * int64(time.Minute)))
|
time.Sleep(time.Duration(*interval * int64(time.Minute)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,359 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/mock/gomock"
|
||||||
|
"src.opensuse.org/autogits/common"
|
||||||
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
|
mock_common "src.opensuse.org/autogits/common/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProcessPR(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
mockGitea := mock_common.NewMockGitea(ctrl)
|
||||||
|
groupName := "testgroup"
|
||||||
|
|
||||||
|
bot := &ReviewBot{
|
||||||
|
gitea: mockGitea,
|
||||||
|
groupName: groupName,
|
||||||
|
}
|
||||||
|
bot.InitRegex(groupName)
|
||||||
|
|
||||||
|
org := "myorg"
|
||||||
|
repo := "myrepo"
|
||||||
|
prIndex := int64(1)
|
||||||
|
headSha := "abcdef123456"
|
||||||
|
|
||||||
|
pr := &models.PullRequest{
|
||||||
|
Index: prIndex,
|
||||||
|
URL: "http://gitea/pr/1",
|
||||||
|
State: "open",
|
||||||
|
Base: &models.PRBranchInfo{
|
||||||
|
Name: "main",
|
||||||
|
Repo: &models.Repository{
|
||||||
|
Name: repo,
|
||||||
|
Owner: &models.User{
|
||||||
|
UserName: org,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Head: &models.PRBranchInfo{
|
||||||
|
Sha: headSha,
|
||||||
|
},
|
||||||
|
User: &models.User{
|
||||||
|
UserName: "submitter",
|
||||||
|
},
|
||||||
|
RequestedReviewers: []*models.User{
|
||||||
|
{UserName: groupName},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
prjConfig := &common.AutogitConfig{
|
||||||
|
GitProjectName: org + "/" + repo + "#main",
|
||||||
|
ReviewGroups: []*common.ReviewGroup{
|
||||||
|
{
|
||||||
|
Name: groupName,
|
||||||
|
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
bot.configs = common.AutogitConfigs{prjConfig}
|
||||||
|
|
||||||
|
t.Run("Review not requested for group", func(t *testing.T) {
|
||||||
|
prNoRequest := *pr
|
||||||
|
prNoRequest.RequestedReviewers = nil
|
||||||
|
err := bot.ProcessPR(&prNoRequest)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PR is closed", func(t *testing.T) {
|
||||||
|
prClosed := *pr
|
||||||
|
prClosed.State = "closed"
|
||||||
|
err := bot.ProcessPR(&prClosed)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Successful Approval", func(t *testing.T) {
|
||||||
|
common.IsDryRun = false
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||||
|
// reviewer1 approved in timeline
|
||||||
|
timeline := []*models.TimelineComment{
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: "reviewer1"},
|
||||||
|
Body: "@" + groupName + ": approve",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||||
|
|
||||||
|
expectedText := "reviewer1 approved a review on behalf of " + groupName
|
||||||
|
mockGitea.EXPECT().AddReviewComment(pr, common.ReviewStateApproved, expectedText).Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().UnrequestReview(org, repo, prIndex, gomock.Any()).Return(nil)
|
||||||
|
|
||||||
|
err := bot.ProcessPR(pr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected nil error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Dry Run - No actions taken", func(t *testing.T) {
|
||||||
|
common.IsDryRun = true
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||||
|
timeline := []*models.TimelineComment{
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: "reviewer1"},
|
||||||
|
Body: "@" + groupName + ": approve",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||||
|
|
||||||
|
// No AddReviewComment or UnrequestReview should be called
|
||||||
|
err := bot.ProcessPR(pr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected nil error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Approval already exists - No new comment", func(t *testing.T) {
|
||||||
|
common.IsDryRun = false
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||||
|
|
||||||
|
approvalText := "reviewer1 approved a review on behalf of " + groupName
|
||||||
|
timeline := []*models.TimelineComment{
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Review,
|
||||||
|
User: &models.User{UserName: groupName},
|
||||||
|
Body: approvalText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: "reviewer1"},
|
||||||
|
Body: "@" + groupName + ": approve",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: groupName},
|
||||||
|
Body: "Help comment",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||||
|
|
||||||
|
// No AddReviewComment, UnrequestReview, or AddComment should be called
|
||||||
|
err := bot.ProcessPR(pr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected nil error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Rejection already exists - No new comment", func(t *testing.T) {
|
||||||
|
common.IsDryRun = false
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||||
|
|
||||||
|
rejectionText := "reviewer1 requested changes on behalf of " + groupName + ". See http://gitea/comment/123"
|
||||||
|
timeline := []*models.TimelineComment{
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Review,
|
||||||
|
User: &models.User{UserName: groupName},
|
||||||
|
Body: rejectionText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: "reviewer1"},
|
||||||
|
Body: "@" + groupName + ": decline",
|
||||||
|
HTMLURL: "http://gitea/comment/123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: groupName},
|
||||||
|
Body: "Help comment",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||||
|
|
||||||
|
err := bot.ProcessPR(pr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected nil error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Pending review - Help comment already exists", func(t *testing.T) {
|
||||||
|
common.IsDryRun = false
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||||
|
|
||||||
|
timeline := []*models.TimelineComment{
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: groupName},
|
||||||
|
Body: "Some help comment",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||||
|
|
||||||
|
// It will try to request reviews
|
||||||
|
mockGitea.EXPECT().RequestReviews(pr, "reviewer1", "reviewer2").Return(nil, nil)
|
||||||
|
|
||||||
|
// AddComment should NOT be called because bot already has a comment in timeline
|
||||||
|
err := bot.ProcessPR(pr)
|
||||||
|
if err != ReviewNotFinished {
|
||||||
|
t.Errorf("Expected ReviewNotFinished error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Submitter is group member - Excluded from review request", func(t *testing.T) {
|
||||||
|
common.IsDryRun = false
|
||||||
|
prSubmitterMember := *pr
|
||||||
|
prSubmitterMember.User = &models.User{UserName: "reviewer1"}
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().RequestReviews(&prSubmitterMember, "reviewer2").Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().AddComment(&prSubmitterMember, gomock.Any()).Return(nil)
|
||||||
|
err := bot.ProcessPR(&prSubmitterMember)
|
||||||
|
if err != ReviewNotFinished {
|
||||||
|
t.Errorf("Expected ReviewNotFinished error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Successful Rejection", func(t *testing.T) {
|
||||||
|
common.IsDryRun = false
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||||
|
timeline := []*models.TimelineComment{
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: "reviewer2"},
|
||||||
|
Body: "@" + groupName + ": decline",
|
||||||
|
HTMLURL: "http://gitea/comment/999",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||||
|
expectedText := "reviewer2 requested changes on behalf of " + groupName + ". See http://gitea/comment/999"
|
||||||
|
mockGitea.EXPECT().AddReviewComment(pr, common.ReviewStateRequestChanges, expectedText).Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().UnrequestReview(org, repo, prIndex, gomock.Any()).Return(nil)
|
||||||
|
err := bot.ProcessPR(pr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected nil error, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Config not found", func(t *testing.T) {
|
||||||
|
bot.configs = common.AutogitConfigs{}
|
||||||
|
err := bot.ProcessPR(pr)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error when config is missing, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Gitea error in GetPullRequestReviews", func(t *testing.T) {
|
||||||
|
bot.configs = common.AutogitConfigs{prjConfig}
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, fmt.Errorf("gitea error"))
|
||||||
|
err := bot.ProcessPR(pr)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error from gitea, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessNotifications(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
mockGitea := mock_common.NewMockGitea(ctrl)
|
||||||
|
groupName := "testgroup"
|
||||||
|
|
||||||
|
bot := &ReviewBot{
|
||||||
|
gitea: mockGitea,
|
||||||
|
groupName: groupName,
|
||||||
|
}
|
||||||
|
bot.InitRegex(groupName)
|
||||||
|
|
||||||
|
org := "myorg"
|
||||||
|
repo := "myrepo"
|
||||||
|
prIndex := int64(123)
|
||||||
|
notificationID := int64(456)
|
||||||
|
|
||||||
|
notification := &models.NotificationThread{
|
||||||
|
ID: notificationID,
|
||||||
|
Subject: &models.NotificationSubject{
|
||||||
|
URL: fmt.Sprintf("http://gitea/api/v1/repos/%s/%s/pulls/%d", org, repo, prIndex),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Notification Success", func(t *testing.T) {
|
||||||
|
common.IsDryRun = false
|
||||||
|
pr := &models.PullRequest{
|
||||||
|
Index: prIndex,
|
||||||
|
Base: &models.PRBranchInfo{
|
||||||
|
Name: "main",
|
||||||
|
Repo: &models.Repository{
|
||||||
|
Name: repo,
|
||||||
|
Owner: &models.User{UserName: org},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Head: &models.PRBranchInfo{
|
||||||
|
Sha: "headsha",
|
||||||
|
Repo: &models.Repository{
|
||||||
|
Name: repo,
|
||||||
|
Owner: &models.User{UserName: org},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
User: &models.User{UserName: "submitter"},
|
||||||
|
RequestedReviewers: []*models.User{{UserName: groupName}},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockGitea.EXPECT().GetPullRequest(org, repo, prIndex).Return(pr, nil)
|
||||||
|
|
||||||
|
prjConfig := &common.AutogitConfig{
|
||||||
|
GitProjectName: org + "/" + repo + "#main",
|
||||||
|
ReviewGroups: []*common.ReviewGroup{{Name: groupName, Reviewers: []string{"r1"}}},
|
||||||
|
}
|
||||||
|
bot.configs = common.AutogitConfigs{prjConfig}
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, prIndex).Return(nil, nil)
|
||||||
|
timeline := []*models.TimelineComment{
|
||||||
|
{
|
||||||
|
Type: common.TimelineCommentType_Comment,
|
||||||
|
User: &models.User{UserName: "r1"},
|
||||||
|
Body: "@" + groupName + ": approve",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, prIndex).Return(timeline, nil)
|
||||||
|
expectedText := "r1 approved a review on behalf of " + groupName
|
||||||
|
mockGitea.EXPECT().AddReviewComment(pr, common.ReviewStateApproved, expectedText).Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().UnrequestReview(org, repo, prIndex, gomock.Any()).Return(nil)
|
||||||
|
|
||||||
|
mockGitea.EXPECT().SetNotificationRead(notificationID).Return(nil)
|
||||||
|
|
||||||
|
bot.ProcessNotifications(notification)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Invalid Notification URL", func(t *testing.T) {
|
||||||
|
badNotification := &models.NotificationThread{
|
||||||
|
Subject: &models.NotificationSubject{
|
||||||
|
URL: "http://gitea/invalid/url",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
bot.ProcessNotifications(badNotification)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Gitea error in GetPullRequest", func(t *testing.T) {
|
||||||
|
mockGitea.EXPECT().GetPullRequest(org, repo, prIndex).Return(nil, fmt.Errorf("gitea error"))
|
||||||
|
bot.ProcessNotifications(notification)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestReviewApprovalCheck(t *testing.T) {
|
func TestReviewApprovalCheck(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -60,16 +413,78 @@ func TestReviewApprovalCheck(t *testing.T) {
|
|||||||
InString: "@group2: disapprove",
|
InString: "@group2: disapprove",
|
||||||
Rejected: true,
|
Rejected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "Whitespace before colon",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "@group : LGTM",
|
||||||
|
Approved: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "No whitespace after colon",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "@group:LGTM",
|
||||||
|
Approved: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Leading and trailing whitespace on line",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: " @group: LGTM ",
|
||||||
|
Approved: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Multiline: Approved on second line",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "Random noise\n@group: approved",
|
||||||
|
Approved: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Multiline: Multiple group mentions, first wins",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "@group: decline\n@group: approve",
|
||||||
|
Rejected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Multiline: Approved on second line",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "noise\n@group: approve\nmore noise",
|
||||||
|
Approved: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Not at start of line (even with whitespace)",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "Hello @group: approve",
|
||||||
|
Approved: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Rejecting with reason",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "@group: decline because of X, Y and Z",
|
||||||
|
Rejected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "No colon after group",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "@group LGTM",
|
||||||
|
Approved: false,
|
||||||
|
Rejected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Invalid char after group",
|
||||||
|
GroupName: "group",
|
||||||
|
InString: "@group! LGTM",
|
||||||
|
Approved: false,
|
||||||
|
Rejected: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
InitRegex(test.GroupName)
|
bot := &ReviewBot{}
|
||||||
|
bot.InitRegex(test.GroupName)
|
||||||
|
|
||||||
if r := ReviewAccepted(test.InString); r != test.Approved {
|
if r := bot.ReviewAccepted(test.InString); r != test.Approved {
|
||||||
t.Error("ReviewAccepted() returned", r, "expecting", test.Approved)
|
t.Error("ReviewAccepted() returned", r, "expecting", test.Approved)
|
||||||
}
|
}
|
||||||
if r := ReviewRejected(test.InString); r != test.Rejected {
|
if r := bot.ReviewRejected(test.InString); r != test.Rejected {
|
||||||
t.Error("ReviewRejected() returned", r, "expecting", test.Rejected)
|
t.Error("ReviewRejected() returned", r, "expecting", test.Rejected)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IssueCommentProcessor struct{}
|
type IssueCommentProcessor struct {
|
||||||
|
bot *ReviewBot
|
||||||
|
}
|
||||||
|
|
||||||
func (s *IssueCommentProcessor) ProcessFunc(req *common.Request) error {
|
func (s *IssueCommentProcessor) ProcessFunc(req *common.Request) error {
|
||||||
if req.Type != common.RequestType_IssueComment {
|
if req.Type != common.RequestType_IssueComment {
|
||||||
@@ -19,14 +21,15 @@ func (s *IssueCommentProcessor) ProcessFunc(req *common.Request) error {
|
|||||||
repo := data.Repository.Name
|
repo := data.Repository.Name
|
||||||
index := int64(data.Issue.Number)
|
index := int64(data.Issue.Number)
|
||||||
|
|
||||||
pr, err := gitea.GetPullRequest(org, repo, index)
|
pr, err := s.bot.gitea.GetPullRequest(org, repo, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to fetch PullRequest from event: %s/%s!%d Error: %w", org, repo, index, err)
|
return fmt.Errorf("Failed to fetch PullRequest from event: %s/%s!%d Error: %w", org, repo, index, err)
|
||||||
}
|
}
|
||||||
return ProcessPR(pr)
|
return s.bot.ProcessPR(pr)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigUpdatePush struct {
|
type ConfigUpdatePush struct {
|
||||||
|
bot *ReviewBot
|
||||||
config_modified chan *common.AutogitConfig
|
config_modified chan *common.AutogitConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +49,7 @@ func (s *ConfigUpdatePush) ProcessFunc(req *common.Request) error {
|
|||||||
}
|
}
|
||||||
branch := data.Ref[len(branch_ref):]
|
branch := data.Ref[len(branch_ref):]
|
||||||
|
|
||||||
c := configs.GetPrjGitConfig(org, repo, branch)
|
c := s.bot.configs.GetPrjGitConfig(org, repo, branch)
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -64,7 +67,7 @@ func (s *ConfigUpdatePush) ProcessFunc(req *common.Request) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if modified_config {
|
if modified_config {
|
||||||
for _, config := range configs {
|
for _, config := range s.bot.configs {
|
||||||
if o, r, _ := config.GetPrjGit(); o == org && r == repo {
|
if o, r, _ := config.GetPrjGit(); o == org && r == repo {
|
||||||
s.config_modified <- config
|
s.config_modified <- config
|
||||||
}
|
}
|
||||||
|
|||||||
203
group-review/rabbit_test.go
Normal file
203
group-review/rabbit_test.go
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/mock/gomock"
|
||||||
|
"src.opensuse.org/autogits/common"
|
||||||
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
|
mock_common "src.opensuse.org/autogits/common/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIssueCommentProcessor(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
mockGitea := mock_common.NewMockGitea(ctrl)
|
||||||
|
groupName := "testgroup"
|
||||||
|
bot := &ReviewBot{
|
||||||
|
gitea: mockGitea,
|
||||||
|
groupName: groupName,
|
||||||
|
}
|
||||||
|
bot.InitRegex(groupName)
|
||||||
|
|
||||||
|
processor := &IssueCommentProcessor{bot: bot}
|
||||||
|
|
||||||
|
org := "myorg"
|
||||||
|
repo := "myrepo"
|
||||||
|
index := 123
|
||||||
|
|
||||||
|
event := &common.IssueCommentWebhookEvent{
|
||||||
|
Repository: &common.Repository{
|
||||||
|
Name: repo,
|
||||||
|
Owner: &common.Organization{
|
||||||
|
Username: org,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Issue: &common.IssueDetail{
|
||||||
|
Number: index,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &common.Request{
|
||||||
|
Type: common.RequestType_IssueComment,
|
||||||
|
Data: event,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Successful Processing", func(t *testing.T) {
|
||||||
|
pr := &models.PullRequest{
|
||||||
|
Index: int64(index),
|
||||||
|
Base: &models.PRBranchInfo{
|
||||||
|
Name: "main",
|
||||||
|
Repo: &models.Repository{
|
||||||
|
Name: repo,
|
||||||
|
Owner: &models.User{UserName: org},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Head: &models.PRBranchInfo{
|
||||||
|
Sha: "headsha",
|
||||||
|
Repo: &models.Repository{
|
||||||
|
Name: repo,
|
||||||
|
Owner: &models.User{UserName: org},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
User: &models.User{UserName: "submitter"},
|
||||||
|
RequestedReviewers: []*models.User{{UserName: groupName}},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockGitea.EXPECT().GetPullRequest(org, repo, int64(index)).Return(pr, nil)
|
||||||
|
|
||||||
|
prjConfig := &common.AutogitConfig{
|
||||||
|
GitProjectName: org + "/" + repo + "#main",
|
||||||
|
ReviewGroups: []*common.ReviewGroup{{Name: groupName, Reviewers: []string{"r1"}}},
|
||||||
|
}
|
||||||
|
bot.configs = common.AutogitConfigs{prjConfig}
|
||||||
|
mockGitea.EXPECT().GetPullRequestReviews(org, repo, int64(index)).Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().GetTimeline(org, repo, int64(index)).Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().RequestReviews(pr, "r1").Return(nil, nil)
|
||||||
|
mockGitea.EXPECT().AddComment(pr, gomock.Any()).Return(nil)
|
||||||
|
|
||||||
|
err := processor.ProcessFunc(req)
|
||||||
|
if err != ReviewNotFinished {
|
||||||
|
t.Errorf("Expected ReviewNotFinished, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Gitea error in GetPullRequest", func(t *testing.T) {
|
||||||
|
mockGitea.EXPECT().GetPullRequest(org, repo, int64(index)).Return(nil, fmt.Errorf("gitea error"))
|
||||||
|
err := processor.ProcessFunc(req)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Wrong Request Type", func(t *testing.T) {
|
||||||
|
wrongReq := &common.Request{Type: common.RequestType_Push}
|
||||||
|
err := processor.ProcessFunc(wrongReq)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error for wrong request type, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigUpdatePush(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
groupName := "testgroup"
|
||||||
|
bot := &ReviewBot{
|
||||||
|
groupName: groupName,
|
||||||
|
}
|
||||||
|
bot.InitRegex(groupName)
|
||||||
|
|
||||||
|
configChan := make(chan *common.AutogitConfig, 1)
|
||||||
|
processor := &ConfigUpdatePush{
|
||||||
|
bot: bot,
|
||||||
|
config_modified: configChan,
|
||||||
|
}
|
||||||
|
|
||||||
|
org := "myorg"
|
||||||
|
repo := "myrepo"
|
||||||
|
branch := "main"
|
||||||
|
|
||||||
|
prjConfig := &common.AutogitConfig{
|
||||||
|
GitProjectName: org + "/" + repo + "#" + branch,
|
||||||
|
Organization: org,
|
||||||
|
Branch: branch,
|
||||||
|
}
|
||||||
|
bot.configs = common.AutogitConfigs{prjConfig}
|
||||||
|
|
||||||
|
event := &common.PushWebhookEvent{
|
||||||
|
Ref: "refs/heads/" + branch,
|
||||||
|
Repository: &common.Repository{
|
||||||
|
Name: repo,
|
||||||
|
Owner: &common.Organization{
|
||||||
|
Username: org,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Commits: []common.Commit{
|
||||||
|
{
|
||||||
|
Modified: []string{common.ProjectConfigFile},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &common.Request{
|
||||||
|
Type: common.RequestType_Push,
|
||||||
|
Data: event,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Config Modified", func(t *testing.T) {
|
||||||
|
err := processor.ProcessFunc(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected nil error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case modified := <-configChan:
|
||||||
|
if modified != prjConfig {
|
||||||
|
t.Errorf("Expected modified config to be %v, got %v", prjConfig, modified)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("Expected config modification signal, but none received")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("No Config Modified", func(t *testing.T) {
|
||||||
|
noConfigEvent := *event
|
||||||
|
noConfigEvent.Commits = []common.Commit{{Modified: []string{"README.md"}}}
|
||||||
|
noConfigReq := &common.Request{Type: common.RequestType_Push, Data: &noConfigEvent}
|
||||||
|
|
||||||
|
err := processor.ProcessFunc(noConfigReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected nil error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-configChan:
|
||||||
|
t.Error("Did not expect config modification signal")
|
||||||
|
default:
|
||||||
|
// Success
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Wrong Branch Ref", func(t *testing.T) {
|
||||||
|
wrongBranchEvent := *event
|
||||||
|
wrongBranchEvent.Ref = "refs/tags/v1.0"
|
||||||
|
wrongBranchReq := &common.Request{Type: common.RequestType_Push, Data: &wrongBranchEvent}
|
||||||
|
|
||||||
|
err := processor.ProcessFunc(wrongBranchReq)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error for wrong branch ref, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Config Not Found", func(t *testing.T) {
|
||||||
|
bot.configs = common.AutogitConfigs{}
|
||||||
|
err := processor.ProcessFunc(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected nil error even if config not found, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -4,11 +4,15 @@ OBS Staging Bot
|
|||||||
Build a PR against a ProjectGit, if review is requested.
|
Build a PR against a ProjectGit, if review is requested.
|
||||||
|
|
||||||
|
|
||||||
Areas of Responsibility
|
Main Tasks
|
||||||
-----------------------
|
----------
|
||||||
|
|
||||||
* Monitors Notification API in Gitea for review requests
|
* A build in OBS is initiated when a review for this bot is requested.
|
||||||
* Reviews Package build results in OBS for all changed packages in ProjectGit PR
|
* The overall build status is reported:
|
||||||
|
* Build successful
|
||||||
|
* Build failed
|
||||||
|
* It checks the build status only for the involved packages compared to the last state of the project for all architectures and all flavors.
|
||||||
|
* It adds an svg with detailed building status.
|
||||||
|
|
||||||
|
|
||||||
Target Usage
|
Target Usage
|
||||||
@@ -21,22 +25,48 @@ Configuration File
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
Bot reads `staging.config` from the project git or the PR to the project git.
|
Bot reads `staging.config` from the project git or the PR to the project git.
|
||||||
It's a JSON file with following syntax
|
It's a JSON file with following syntax:
|
||||||
|
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
"ObsProject": "home:foo:project",
|
"ObsProject": "SUSE:SLFO:1.2",
|
||||||
"StagingProject": "home:foo:project:staging",
|
"StagingProject": "SUSE:SLFO:1.2:PullRequest",
|
||||||
"QA": [
|
"QA": [
|
||||||
{
|
{
|
||||||
"Name": "ProjectBuild",
|
"Name": "SLES",
|
||||||
"Origin": "home:foo:product:images"
|
"Origin": "SUSE:SLFO:Products:SLES:16.0",
|
||||||
}
|
"BuildDisableRepos": ["product"]
|
||||||
]
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* ObsProject: (**required**) Project where the base project is built. Builds in this project will be used to compare to builds based on sources from the PR
|
| Field name | Details | Mandatory | Type | Allowed Values | Default |
|
||||||
* StagingProject: template project that will be used as template for the staging project. Omitting this will use the ObsProject repositories to create the staging. Staging project will be created under the template, or in the bot's home directory if not specified.
|
| ----- | ----- | ----- | ----- | ----- | ----- |
|
||||||
* QA: set of projects to build ontop of the binaries built in staging.
|
| *ObsProject* | Product OBS project. Builds in this project will be used to compare to builds based on sources from the PR. | yes | string | `[a-zA-Z0-9-_:]+` | |
|
||||||
|
| *StagingProject* | Used both as base project and prefix for all OBS staging projects. Upon being added as a reviewer to a PrjGit PR, this bot automatically generates an OBS project named *StagingProject:<PR_Number>*. It must be a sub-project of the *ObsProject*. | yes | string | `[a-zA-Z0-9-_:]+` | |
|
||||||
|
| *QA* | Crucial for generating a product build (such as an ISO or FTP tree) that incorporates the packages. | no | array of objects | | |
|
||||||
|
| *QA > Name* | Suffix for the QA OBS staging project. The project is named *StagingProject:<PR_Number>:Name*. | no | string | | |
|
||||||
|
| *QA > Origin* | OBS reference project | no | string | | |
|
||||||
|
| *QA > BuildDisableRepos* | The names of OBS repositories to build-disable, if any. | no | array of strings | | [] |
|
||||||
|
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
|
* **OBS staging projects are deleted** when the relative PrjGit PR is closed or merged.
|
||||||
|
|
||||||
|
* **PrjGit PR - staging project**
|
||||||
|
* The OBS staging project utilizes an **scmsync** tag, configured with the `onlybuild` flag, to exclusively build packages associated with this specific PrjGit PR.
|
||||||
|
* The **build config** is inherited from the PrjGit PR config file (even if unchanged).
|
||||||
|
* The **project meta** creates a standard repository following the StagingProject as a project path.
|
||||||
|
* The base *StagingProject* has the macro **FromScratch:** set in its config, which prevents inheriting the configuration from the included project paths.
|
||||||
|
* The bot copies the project maintainers from *StagingProject* to the specific staging project (*StagingProject:<PR_Number>*).
|
||||||
|
* The bot reports “Build successful” only if the build is successful for all repositories and all architectures.
|
||||||
|
|
||||||
|
* **PrjGit PR - QA staging project**
|
||||||
|
* The QA staging project is meant for building the product; the relative build config is inherited from the `QA > Origin` project.
|
||||||
|
* In this case, the **scmsync** tag is inherited from the `QA > Origin` project.
|
||||||
|
* It is desirable in some cases to avoid building some specific build service repositories when not needed. In this case, `QA > BuildDisableRepos` can be specified.
|
||||||
|
These repositories would be disabled in the project meta when generating the QA project.
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ const (
|
|||||||
BuildStatusSummaryUnknown = 4
|
BuildStatusSummaryUnknown = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DisableFlag struct {
|
||||||
|
XMLName string `xml:"disable"`
|
||||||
|
Name string `xml:"repository,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
|
func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
|
||||||
if _, finished := refProject.BuildResultSummary(); !finished {
|
if _, finished := refProject.BuildResultSummary(); !finished {
|
||||||
common.LogDebug("refProject not finished building??")
|
common.LogDebug("refProject not finished building??")
|
||||||
@@ -377,7 +382,7 @@ func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque
|
|||||||
// stagingProject:$buildProject
|
// stagingProject:$buildProject
|
||||||
// ^- stagingProject:$buildProject:$subProjectName (based on templateProject)
|
// ^- stagingProject:$buildProject:$subProjectName (based on templateProject)
|
||||||
|
|
||||||
func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingProject, templateProject, subProjectName string) error {
|
func CreateQASubProject(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("Setup QA sub projects")
|
||||||
templateMeta, err := ObsClient.GetProjectMeta(templateProject)
|
templateMeta, err := ObsClient.GetProjectMeta(templateProject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -407,7 +412,21 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
|
|||||||
repository.Fragment = branch.SHA
|
repository.Fragment = branch.SHA
|
||||||
templateMeta.ScmSync = repository.String()
|
templateMeta.ScmSync = repository.String()
|
||||||
common.LogDebug("Setting scmsync url to ", templateMeta.ScmSync)
|
common.LogDebug("Setting scmsync url to ", templateMeta.ScmSync)
|
||||||
}
|
}
|
||||||
|
// Build-disable repositories if asked
|
||||||
|
if len(buildDisableRepos) > 0 {
|
||||||
|
toDisable := make([]DisableFlag, len(buildDisableRepos))
|
||||||
|
for idx, repositoryName := range buildDisableRepos {
|
||||||
|
toDisable[idx] = DisableFlag{Name: repositoryName}
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := xml.Marshal(toDisable)
|
||||||
|
if err != nil {
|
||||||
|
common.LogError("error while marshalling, skipping BuildDisableRepos: ", err)
|
||||||
|
} else {
|
||||||
|
templateMeta.BuildFlags.Contents += string(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Cleanup ReleaseTarget and modify affected path entries
|
// Cleanup ReleaseTarget and modify affected path entries
|
||||||
for idx, r := range templateMeta.Repositories {
|
for idx, r := range templateMeta.Repositories {
|
||||||
templateMeta.Repositories[idx].ReleaseTargets = nil
|
templateMeta.Repositories[idx].ReleaseTargets = nil
|
||||||
@@ -922,7 +941,8 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
|
|||||||
CreateQASubProject(stagingConfig, git, gitea, pr,
|
CreateQASubProject(stagingConfig, git, gitea, pr,
|
||||||
stagingProject,
|
stagingProject,
|
||||||
setup.Origin,
|
setup.Origin,
|
||||||
setup.Name)
|
setup.Name,
|
||||||
|
setup.BuildDisableRepos)
|
||||||
msg = msg + ObsWebHost + "/project/show/" +
|
msg = msg + ObsWebHost + "/project/show/" +
|
||||||
stagingProject + ":" + setup.Name + "\n"
|
stagingProject + ":" + setup.Name + "\n"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,41 @@
|
|||||||
Direct Workflow bot
|
Direct Workflow bot
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
The project submodule is automatically updated by the direct bot whenever a branch is updated in a package repository.
|
||||||
|
This bot can coexist with the Workflow PR bot, which is instead triggered by a new package PR.
|
||||||
|
|
||||||
|
Target Usage
|
||||||
|
------------
|
||||||
|
|
||||||
|
Devel project, where direct pushes to package git are possible.
|
||||||
|
|
||||||
Areas of responsibility
|
Areas of responsibility
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
1. Keep ProjectGit in sync with packages in the organization
|
1. Keep ProjectGit in sync with packages in the organization
|
||||||
* on pushes to package, updates the submodule commit id
|
* **On pushes to package**: updates the submodule commit ID to the default branch HEAD (as configured in Gitea).
|
||||||
to the default branch HEAD (as configured in Gitea)
|
* **On repository adds**: creates a new submodule (if non-empty).
|
||||||
* on repository adds, creates a new submodule (if non empty)
|
* **On repository removal**: removes the submodule.
|
||||||
* on repository removal, removes the submodule
|
|
||||||
|
**Note:** If you want to revert a change in a package, you need to do that manually in the project git.
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Uses `workflow.config` for configuration. Parameters
|
Uses `workflow.config` for configuration.
|
||||||
|
|
||||||
* _Workflows_: ["direct"] -- direct entry enables direct workflow. **Mandatory**
|
| Field name | Details | Mandatory | Type | Allowed Values | Default |
|
||||||
* _Organization_: organization that holds all the packages. **Mandatory**
|
| ----- | ----- | ----- | ----- | ----- | ----- |
|
||||||
* _Branch_: branch updated in repo's, or blank for default package branch
|
| *Workflows* | Type of workflow | yes | string | “direct” | |
|
||||||
* _GitProjectName_: package in above org, or `org/package#branch` for PrjGit. By default assumes `_ObsPrj` with default branch and in the `Organization`
|
| *Organization* | The organization that holds all the packages | yes | string | | |
|
||||||
|
| *Branch* | The designated branch for packages | no | string | | blank (default package branch) |
|
||||||
|
| *GitProjectName* | Repository and branch where the ProjectGit lives. | no | string | **Format**: `org/project_repo#branch` | By default assumes `_ObsPrj` with default branch in the *Organization* |
|
||||||
|
|
||||||
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.
|
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
|
|
||||||
|
Environment Variables
|
||||||
-------
|
-------
|
||||||
|
|
||||||
* `GITEA_TOKEN` (required)
|
* `GITEA_TOKEN` (required)
|
||||||
@@ -37,8 +49,3 @@ Running
|
|||||||
* `AUTOGITS_REPO_PATH` - default is temporary directory
|
* `AUTOGITS_REPO_PATH` - default is temporary directory
|
||||||
* `AUTOGITS_IDENTITY_FILE` - in case where we need explicit identify path for ssh specified
|
* `AUTOGITS_IDENTITY_FILE` - in case where we need explicit identify path for ssh specified
|
||||||
|
|
||||||
Target Usage
|
|
||||||
------------
|
|
||||||
|
|
||||||
Devel project, where direct pushes to package git are possible
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,65 @@
|
|||||||
Workflow-PR bot
|
Workflow-PR bot
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Keeps ProjectGit PR in-sync with a PackageGit PR
|
Keeps ProjectGit PRs in-sync with the relative PackageGit PRs.
|
||||||
|
|
||||||
|
|
||||||
Areas of Responsibility
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
* Detects a PackageGit PR creation against a package and creates a coresponsing PR against the ProjectGit
|
|
||||||
* When a PackageGit PR is updated, the corresponding PR against the ProjectGit is updated
|
|
||||||
* Stores reference to the PackageGit PR in the headers of the ProjectGit PR comments, for later reference
|
|
||||||
* this allows ProjectGit PR to be merged to seperated later (via another tool, for example)
|
|
||||||
* Initiates all staging workflows via review requests
|
|
||||||
|
|
||||||
|
|
||||||
Target Usage
|
Target Usage
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Any project (devel, etc) that accepts PR
|
Any project (devel, codestream, product, etc.) that accepts PRs.
|
||||||
|
|
||||||
|
|
||||||
|
Main Tasks
|
||||||
|
----------
|
||||||
|
|
||||||
|
* **Synchronization**:
|
||||||
|
* When a **PackageGit PR** is created for a package on a specific project branch, a corresponding PR is automatically generated in **ProjectGit**.
|
||||||
|
* When a PackageGit PR is updated, the corresponding PR against the ProjectGit is updated.
|
||||||
|
* A link to the PackageGit PR is stored in the body of the ProjectGit PR comments in the following format:
|
||||||
|
* `PR: organization/package_name!pull_request_number`
|
||||||
|
* Example: `PR: pool/curl!4`
|
||||||
|
* It closes an empty ProjectGit PR (e.g., if a PR was initially created for a single package but later integrated into a larger ProjectGit PR).
|
||||||
|
* It forwards the Work In Progress (WIP) flag to the ProjectGit PR. If the ProjectGit PR references multiple Package PRs, triggering the WIP flag on the ProjectGit PR side only requires a single WIP package PR.
|
||||||
|
|
||||||
|
* **Reviewer Management**:
|
||||||
|
* It adds required reviewers in the ProjectGit PR.
|
||||||
|
* It adds required reviewers in the PackageGit PR.
|
||||||
|
* If new commits are added to a PackageGit PR, reviewers who have already approved it will be re-added.
|
||||||
|
|
||||||
|
* **Merge Management**:
|
||||||
|
* Manages PR merges based on configuration flags (`ManualMergeOnly`, `ManualMergeProject`).
|
||||||
|
* In general, merge only happens if all mandatory reviews are completed.
|
||||||
|
* **ManualMergeProject** is stricter than **ManualMergeOnly** and has higher priority.
|
||||||
|
|
||||||
|
| Flag | Value | Behavior |
|
||||||
|
| ----- | ----- | ----- |
|
||||||
|
| ManualMergeProject | true | Both ProjectGit and PackageGit PRs are merged upon an allowed project maintainer commenting "merge ok” in the ProjectGit PR. |
|
||||||
|
| ManualMergeOnly | true | Both PackageGit PR and ProjectGit PR are merged upon an allowed package maintainer or project maintainer commenting “merge ok” in the PackageGit PR. |
|
||||||
|
| ManualMergeOnly and ManualMergeProject | false | Both ProjectGit and PackageGit PRs are merged as soon as all reviews are completed in both PrjGit and PkgGit PRs. |
|
||||||
|
|
||||||
Config file
|
Config file
|
||||||
-----------
|
-----------
|
||||||
JSON
|
|
||||||
* _Workflows_: ["pr"] -- pr entry enables pr workflow. **Mandatory**
|
|
||||||
* _Organization_: organization that holds all the packages **Mandatory**
|
|
||||||
* _Branch_: branch updated in repo's **Mandatory**
|
|
||||||
* _GitProjectName_: package in above org, or `org/package#branch` for PrjGit. By default assumes `_ObsPrj` with default branch and in the `Organization`
|
|
||||||
* _Reviewers_: accounts associated with mandatory reviews for PrjGit. Can trigger additional
|
|
||||||
review requests for PrjGit or associated PkgGit repos. Only when all reviews are
|
|
||||||
satisfied, will the PrjGit PR be merged. See Reviewers below.
|
|
||||||
* _ManualMergeOnly_: (true, false) only merge if "merge ok" comment/review by package or project maintainers or reviewers
|
|
||||||
* _ManualMergeProject_: (true, false) only merge if "merge ok" by project maintainers or reviewers
|
|
||||||
* _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.
|
* Filename: `workflow.config`
|
||||||
example:
|
* Location: ProjectGit
|
||||||
|
* Format: non-standard JSON (comments allowed)
|
||||||
|
|
||||||
|
| Field name | Details | Mandatory | Type | Allowed Values | Default |
|
||||||
|
| ----- | ----- | ----- | ----- | ----- | ----- |
|
||||||
|
| *Workflows* | Type of workflow | yes | string | “pr” | |
|
||||||
|
| *Organization* | The organization where PackageGit PRs are expected to occur | yes | string | | |
|
||||||
|
| *Branch* | The designated branch for PackageGit PRs | yes | string | | |
|
||||||
|
| *GitProjectName* | Repository and branch where the ProjectGit lives. | no | string | **Format**: `org/project_repo#branch` | By default assumes `_ObsPrj` with default branch in the *Organization* |
|
||||||
|
| *ManualMergeOnly* | Merges are permitted only upon receiving a "merge ok" comment from designated maintainers in the PkgGit PR. | no | bool | true, false | false |
|
||||||
|
| *ManualMergeProject* | Merges are permitted only upon receiving a "merge ok" comment in the ProjectGit PR from project maintainers. | no | bool | true, false | false |
|
||||||
|
| *ReviewRequired* | (NOT IMPLEMENTED) If submitter is a maintainer, require review from another maintainer if available. | no | bool | true, false | false |
|
||||||
|
| *NoProjectGitPR* | Do not create PrjGit PR, but still perform other tasks. | no | bool | true, false | false |
|
||||||
|
| *Reviewers* | PrjGit reviewers. Additional review requests are triggered for associated PkgGit PRs. PrjGit PR is merged only when all reviews are complete. | no | array of strings | | `[]` |
|
||||||
|
| *ReviewGroups* | If a group is specified in Reviewers, its members are listed here. | no | array of objects | | `[]` |
|
||||||
|
| *ReviewGroups > Name* | Name of the group | no | string | | |
|
||||||
|
| *ReviewGroups > Reviewers* | Members of the group | no | array of strings | | |
|
||||||
|
| *ReviewGroups > Silent* | Add members for notifications. If true, members are not explicitly requested to review. If one member approves, others are removed. | no | bool | true, false | false |
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"Workflows": ["pr", "direct"],
|
|
||||||
"Organization": "autogits",
|
|
||||||
"GitProjectName": "HiddenPrj",
|
|
||||||
"Branch": "hidden",
|
|
||||||
"Reviewers": []
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
Reviewers
|
Reviewers
|
||||||
---------
|
---------
|
||||||
@@ -58,21 +68,30 @@ Reviews is a list of accounts that need to review package and/or project. They h
|
|||||||
|
|
||||||
[~][*|-|+]username
|
[~][*|-|+]username
|
||||||
|
|
||||||
General prefix of ~ indicates advisory reviewer. They will be requested, but ignored otherwise.
|
A tilde (`~`) before a prefix signifies an advisory reviewer. Their input is requested, but their review status will not otherwise affect the process.
|
||||||
|
|
||||||
Other prefixes indicate project or package association of the reviewer:
|
Other prefixes indicate project or package association of the reviewer:
|
||||||
|
|
||||||
* `*` indicates project *and* package
|
* `*` indicates project *and* package
|
||||||
* `-` indicates project-only reviewer
|
* `-` indicates project-only reviewer
|
||||||
* `+` indicates package-only reviewer
|
* `+` indicates package-only reviewer
|
||||||
|
|
||||||
`+` is implied. For example
|
`+` is implied.
|
||||||
|
|
||||||
`[foo, -bar, ~*moo]`
|
For example: `[foo, -bar, ~*moo]` results in:
|
||||||
|
* foo: package reviews
|
||||||
|
* bar: project reviews
|
||||||
|
* moo: package and project reviews, but ignored
|
||||||
|
|
||||||
results in
|
Package Deletion Requests
|
||||||
* foo -> package reviews
|
-------------------------
|
||||||
* bar -> project reviews
|
(NOT YET IMPLEMENTED)
|
||||||
* moo -> package and project reviews, but ignored
|
|
||||||
|
* **Removing a Package:**
|
||||||
|
To remove a package from a project, submit a ProjectGit Pull Request (PR) that removes the corresponding submodule. The bot will then rename the project branch in the pool by appending "-removed" to its name.
|
||||||
|
|
||||||
|
* **Adding a Package Again:**
|
||||||
|
If you wish to re-add a package, create a new PrjGit PR which adds again the submodule on the branch that has the "-removed" suffix. The bot will automatically remove this suffix from the project branch in the pool.
|
||||||
|
|
||||||
|
|
||||||
Labels
|
Labels
|
||||||
@@ -83,26 +102,38 @@ The following labels are used, when defined in Repo/Org.
|
|||||||
| Label Config Entry | Default label | Description
|
| Label Config Entry | Default label | Description
|
||||||
|--------------------|----------------|----------------------------------------
|
|--------------------|----------------|----------------------------------------
|
||||||
| StagingAuto | staging/Auto | Assigned to Project Git PRs when first staged
|
| StagingAuto | staging/Auto | Assigned to Project Git PRs when first staged
|
||||||
| ReviewPending | review/Pending | Assigned to PR when reviews are still pending
|
| ReviewPending | review/Pending | Assigned to Project Git PR when package reviews are still pending
|
||||||
| ReviewDone | review/Done | Assigned to PR when reviews are complete on this particular PR
|
| ReviewDone | review/Done | Assigned to Project Git PR when reviews are complete on all package PRs
|
||||||
|
|
||||||
|
|
||||||
Maintainership
|
Maintainership
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Maintainership information is defined per project. For reviews, package maintainers are coalesced
|
Filename: \_maintainership.json
|
||||||
with project maintainers. A review by any of the maintainers is acceptable.
|
Location: ProjectGit
|
||||||
|
Format: JSON
|
||||||
|
Fields:
|
||||||
|
|
||||||
example:
|
| Key | Value | Notes |
|
||||||
|
| ----- | ----- | ----- |
|
||||||
|
| package name | array of strings representing the package maintainers | List of package maintainers |
|
||||||
|
| “” (empty string) | array of strings representing the project maintainers | List of project maintainers |
|
||||||
|
|
||||||
{
|
Maintainership information is defined per project. For PackageGit PR reviews, package maintainers are combined with project maintainers. A review by any of these maintainers is acceptable.
|
||||||
"package1": [ "reviewer", "reviewer2"],
|
|
||||||
"package2": [],
|
|
||||||
|
|
||||||
// "project" maintainer
|
If the submitter is a maintainer it will not get a review requested.
|
||||||
"": ["reviewer3", "reviewer4"]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"package1": [ "reviewer", "reviewer2"],
|
||||||
|
"package2": [],
|
||||||
|
|
||||||
|
// "project" maintainer
|
||||||
|
"": ["reviewer3", "reviewer4"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Permissions
|
Permissions
|
||||||
-----------
|
-----------
|
||||||
@@ -122,3 +153,11 @@ the `workflow.config`.
|
|||||||
|
|
||||||
NOTE: Project Maintainers have these permissions automatically.
|
NOTE: Project Maintainers have these permissions automatically.
|
||||||
|
|
||||||
|
Server configuration
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
**Configuration file:**
|
||||||
|
|
||||||
|
| Field | Type | Notes |
|
||||||
|
| ----- | ----- | ----- |
|
||||||
|
| root | Array of string | Format **org/repo\#branch** |
|
||||||
18
workflow-pr/interfaces/state_checker.go
Normal file
18
workflow-pr/interfaces/state_checker.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package interfaces
|
||||||
|
|
||||||
|
import "src.opensuse.org/autogits/common"
|
||||||
|
|
||||||
|
//go:generate mockgen -source=state_checker.go -destination=../mock/state_checker.go -typed -package mock_main
|
||||||
|
|
||||||
|
|
||||||
|
type StateChecker interface {
|
||||||
|
VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error)
|
||||||
|
CheckRepos()
|
||||||
|
ConsistencyCheckProcess() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type PRToProcess struct {
|
||||||
|
Org, Repo, Branch string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -146,19 +146,15 @@ func main() {
|
|||||||
num, err := strconv.ParseInt(data[3], 10, 64)
|
num, err := strconv.ParseInt(data[3], 10, 64)
|
||||||
common.LogInfo("Processing:", org, "/", repo, "#", num)
|
common.LogInfo("Processing:", org, "/", repo, "#", num)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
if pr, err := Gitea.GetPullRequest(org, repo, num); err == nil && pr != nil {
|
pr, err := Gitea.GetPullRequest(org, repo, num)
|
||||||
if err = ProcesPullRequest(pr, configs); err != nil {
|
if err != nil {
|
||||||
common.LogError("PR processor returned error", err)
|
common.LogError("Cannot fetch PR", err)
|
||||||
}
|
|
||||||
} else if issue, err := Gitea.GetIssue(org, repo, num); err == nil && issue != nil {
|
|
||||||
if err = ProcessIssue(issue, configs); err != nil {
|
|
||||||
common.LogError("issue processor returned error:", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
common.LogError("Cannot fetch PR or Issue", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = ProcesPullRequest(pr, configs); err != nil {
|
||||||
|
common.LogError("processor returned error", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -20,6 +22,83 @@ func TestProjectBranchName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LocalCMD = "---"
|
||||||
|
|
||||||
|
func gitExecs(t *testing.T, git *common.GitHandlerImpl, cmds [][]string) {
|
||||||
|
for _, cmd := range cmds {
|
||||||
|
if cmd[0] == LocalCMD {
|
||||||
|
command := exec.Command(cmd[2], cmd[3:]...)
|
||||||
|
command.Dir = filepath.Join(git.GitPath, cmd[1])
|
||||||
|
command.Stdin = nil
|
||||||
|
command.Env = append([]string{"GIT_CONFIG_COUNT=1", "GIT_CONFIG_KEY_1=protocol.file.allow", "GIT_CONFIG_VALUE_1=always"}, common.ExtraGitParams...)
|
||||||
|
_, err := command.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(" *** error: %v\n", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
git.GitExecOrPanic(cmd[0], cmd[1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandsForPackages(dir, prefix string, startN, endN int) [][]string {
|
||||||
|
commands := make([][]string, (endN-startN+2)*6)
|
||||||
|
|
||||||
|
if dir == "" {
|
||||||
|
dir = "."
|
||||||
|
}
|
||||||
|
cmdIdx := 0
|
||||||
|
for idx := startN; idx <= endN; idx++ {
|
||||||
|
pkgDir := fmt.Sprintf("%s%d", prefix, idx)
|
||||||
|
|
||||||
|
commands[cmdIdx+0] = []string{"", "init", "-q", "--object-format", "sha256", "-b", "testing", pkgDir}
|
||||||
|
commands[cmdIdx+1] = []string{LocalCMD, pkgDir, "/usr/bin/touch", "testFile"}
|
||||||
|
commands[cmdIdx+2] = []string{pkgDir, "add", "testFile"}
|
||||||
|
commands[cmdIdx+3] = []string{pkgDir, "commit", "-m", "added testFile"}
|
||||||
|
commands[cmdIdx+4] = []string{pkgDir, "config", "receive.denyCurrentBranch", "ignore"}
|
||||||
|
commands[cmdIdx+5] = []string{"prj", "submodule", "add", filepath.Join("..", pkgDir), filepath.Join(dir, pkgDir)}
|
||||||
|
|
||||||
|
cmdIdx += 6
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all the submodules to the prj
|
||||||
|
commands[cmdIdx+0] = []string{"prj", "commit", "-a", "-m", "adding subpackages"}
|
||||||
|
|
||||||
|
return commands
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupGitForTests(t *testing.T, git *common.GitHandlerImpl) {
|
||||||
|
common.ExtraGitParams = []string{
|
||||||
|
"GIT_CONFIG_COUNT=1",
|
||||||
|
"GIT_CONFIG_KEY_0=protocol.file.allow",
|
||||||
|
"GIT_CONFIG_VALUE_0=always",
|
||||||
|
|
||||||
|
"GIT_AUTHOR_NAME=testname",
|
||||||
|
"GIT_AUTHOR_EMAIL=test@suse.com",
|
||||||
|
"GIT_AUTHOR_DATE='2005-04-07T22:13:13'",
|
||||||
|
"GIT_COMMITTER_NAME=testname",
|
||||||
|
"GIT_COMMITTER_EMAIL=test@suse.com",
|
||||||
|
"GIT_COMMITTER_DATE='2005-04-07T22:13:13'",
|
||||||
|
}
|
||||||
|
|
||||||
|
gitExecs(t, git, [][]string{
|
||||||
|
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "prj"},
|
||||||
|
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "foo"},
|
||||||
|
{LocalCMD, "foo", "/usr/bin/touch", "file1"},
|
||||||
|
{"foo", "add", "file1"},
|
||||||
|
{"foo", "commit", "-m", "first commit"},
|
||||||
|
{"prj", "config", "receive.denyCurrentBranch", "ignore"},
|
||||||
|
{"prj", "submodule", "init"},
|
||||||
|
{"prj", "submodule", "add", "../foo", "testRepo"},
|
||||||
|
{"prj", "add", ".gitmodules", "testRepo"},
|
||||||
|
{"prj", "commit", "-m", "First instance"},
|
||||||
|
{"prj", "submodule", "deinit", "testRepo"},
|
||||||
|
{LocalCMD, "foo", "/usr/bin/touch", "file2"},
|
||||||
|
{"foo", "add", "file2"},
|
||||||
|
{"foo", "commit", "-m", "added file2"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpdatePrBranch(t *testing.T) {
|
func TestUpdatePrBranch(t *testing.T) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
origLogger := log.Writer()
|
origLogger := log.Writer()
|
||||||
@@ -46,7 +125,7 @@ func TestUpdatePrBranch(t *testing.T) {
|
|||||||
req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
|
req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
|
||||||
req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0])
|
req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0])
|
||||||
|
|
||||||
updateSubmoduleInPR("testRepo", revs[0], git)
|
updateSubmoduleInPR("mainRepo", revs[0], git)
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit"))
|
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit"))
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:+testing"))
|
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:+testing"))
|
||||||
git.GitExecOrPanic("prj", "reset", "--hard", "testing")
|
git.GitExecOrPanic("prj", "reset", "--hard", "testing")
|
||||||
|
|||||||
10
workflow-pr/mock/pr_processor.go
Normal file
10
workflow-pr/mock/pr_processor.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: pr_processor.go
|
||||||
|
//
|
||||||
|
// Generated by this command:
|
||||||
|
//
|
||||||
|
// mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
|
||||||
|
//
|
||||||
|
|
||||||
|
// Package mock_main is a generated GoMock package.
|
||||||
|
package mock_main
|
||||||
@@ -3,18 +3,18 @@
|
|||||||
//
|
//
|
||||||
// Generated by this command:
|
// Generated by this command:
|
||||||
//
|
//
|
||||||
// mockgen -source=state_checker.go -destination=mock_state_checker.go -typed -package main
|
// mockgen -source=state_checker.go -destination=../mock/state_checker.go -typed -package mock_main
|
||||||
//
|
//
|
||||||
|
|
||||||
// Package main is a generated GoMock package.
|
// Package mock_main is a generated GoMock package.
|
||||||
package main
|
package mock_main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
gomock "go.uber.org/mock/gomock"
|
gomock "go.uber.org/mock/gomock"
|
||||||
common "src.opensuse.org/autogits/common"
|
common "src.opensuse.org/autogits/common"
|
||||||
models "src.opensuse.org/autogits/common/gitea-generated/models"
|
interfaces "src.opensuse.org/autogits/workflow-pr/interfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockStateChecker is a mock of StateChecker interface.
|
// MockStateChecker is a mock of StateChecker interface.
|
||||||
@@ -42,9 +42,11 @@ func (m *MockStateChecker) EXPECT() *MockStateCheckerMockRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckRepos mocks base method.
|
// CheckRepos mocks base method.
|
||||||
func (m *MockStateChecker) CheckRepos() {
|
func (m *MockStateChecker) CheckRepos() error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
m.ctrl.Call(m, "CheckRepos")
|
ret := m.ctrl.Call(m, "CheckRepos")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckRepos indicates an expected call of CheckRepos.
|
// CheckRepos indicates an expected call of CheckRepos.
|
||||||
@@ -60,19 +62,19 @@ type MockStateCheckerCheckReposCall struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockStateCheckerCheckReposCall) Return() *MockStateCheckerCheckReposCall {
|
func (c *MockStateCheckerCheckReposCall) Return(arg0 error) *MockStateCheckerCheckReposCall {
|
||||||
c.Call = c.Call.Return()
|
c.Call = c.Call.Return(arg0)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockStateCheckerCheckReposCall) Do(f func()) *MockStateCheckerCheckReposCall {
|
func (c *MockStateCheckerCheckReposCall) Do(f func() error) *MockStateCheckerCheckReposCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockStateCheckerCheckReposCall) DoAndReturn(f func()) *MockStateCheckerCheckReposCall {
|
func (c *MockStateCheckerCheckReposCall) DoAndReturn(f func() error) *MockStateCheckerCheckReposCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -116,10 +118,10 @@ func (c *MockStateCheckerConsistencyCheckProcessCall) DoAndReturn(f func() error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VerifyProjectState mocks base method.
|
// VerifyProjectState mocks base method.
|
||||||
func (m *MockStateChecker) VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error) {
|
func (m *MockStateChecker) VerifyProjectState(configs *common.AutogitConfig) ([]*interfaces.PRToProcess, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "VerifyProjectState", configs)
|
ret := m.ctrl.Call(m, "VerifyProjectState", configs)
|
||||||
ret0, _ := ret[0].([]*PRToProcess)
|
ret0, _ := ret[0].([]*interfaces.PRToProcess)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
@@ -137,81 +139,19 @@ type MockStateCheckerVerifyProjectStateCall struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockStateCheckerVerifyProjectStateCall) Return(arg0 []*PRToProcess, arg1 error) *MockStateCheckerVerifyProjectStateCall {
|
func (c *MockStateCheckerVerifyProjectStateCall) Return(arg0 []*interfaces.PRToProcess, arg1 error) *MockStateCheckerVerifyProjectStateCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockStateCheckerVerifyProjectStateCall) Do(f func(*common.AutogitConfig) ([]*PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
func (c *MockStateCheckerVerifyProjectStateCall) Do(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPullRequestProcessor is a mock of PullRequestProcessor interface.
|
|
||||||
type MockPullRequestProcessor struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockPullRequestProcessorMockRecorder
|
|
||||||
isgomock struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPullRequestProcessorMockRecorder is the mock recorder for MockPullRequestProcessor.
|
|
||||||
type MockPullRequestProcessorMockRecorder struct {
|
|
||||||
mock *MockPullRequestProcessor
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockPullRequestProcessor creates a new mock instance.
|
|
||||||
func NewMockPullRequestProcessor(ctrl *gomock.Controller) *MockPullRequestProcessor {
|
|
||||||
mock := &MockPullRequestProcessor{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockPullRequestProcessorMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockPullRequestProcessor) EXPECT() *MockPullRequestProcessorMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process mocks base method.
|
|
||||||
func (m *MockPullRequestProcessor) Process(req *models.PullRequest) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Process", req)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process indicates an expected call of Process.
|
|
||||||
func (mr *MockPullRequestProcessorMockRecorder) Process(req any) *MockPullRequestProcessorProcessCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*MockPullRequestProcessor)(nil).Process), req)
|
|
||||||
return &MockPullRequestProcessorProcessCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPullRequestProcessorProcessCall wrap *gomock.Call
|
|
||||||
type MockPullRequestProcessorProcessCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockPullRequestProcessorProcessCall) Return(arg0 error) *MockPullRequestProcessorProcessCall {
|
|
||||||
c.Call = c.Call.Return(arg0)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockPullRequestProcessorProcessCall) Do(f func(*models.PullRequest) error) *MockPullRequestProcessorProcessCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockPullRequestProcessorProcessCall) DoAndReturn(f func(*models.PullRequest) error) *MockPullRequestProcessorProcessCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
//go:generate mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -190,14 +192,7 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !submodule_found {
|
if !submodule_found {
|
||||||
common.LogInfo("Adding new submodule", repo, "to PrjGit")
|
common.LogError("Failed to find expected repo:", repo)
|
||||||
ref := fmt.Sprintf(common.PrPattern, org, repo, idx)
|
|
||||||
commitMsg := fmt.Sprintln("Add package", repo, "\n\nThis commit was autocreated by", GitAuthor, "\n\nreferencing PRs:\n", ref)
|
|
||||||
|
|
||||||
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "add", "-b", pr.PR.Base.Name, pr.PR.Base.Repo.SSHURL, repo)
|
|
||||||
|
|
||||||
updateSubmoduleInPR(repo, prHead, git)
|
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -605,7 +600,7 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
|
|||||||
common.LogError("merge error:", err)
|
common.LogError("merge error:", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = prset.AssignReviewers(Gitea, maintainers)
|
prset.AssignReviewers(Gitea, maintainers)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -615,14 +610,6 @@ type RequestProcessor struct {
|
|||||||
recursive int
|
recursive int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *RequestProcessor) Process(pr *models.PullRequest) error {
|
|
||||||
configs, ok := w.configuredRepos[pr.Base.Repo.Owner.UserName]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("no config found for org %s", pr.Base.Repo.Owner.UserName)
|
|
||||||
}
|
|
||||||
return ProcesPullRequest(pr, configs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig) error {
|
func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig) error {
|
||||||
if len(configs) < 1 {
|
if len(configs) < 1 {
|
||||||
// ignoring pull request against unconfigured project (could be just regular sources?)
|
// ignoring pull request against unconfigured project (could be just regular sources?)
|
||||||
@@ -639,186 +626,6 @@ func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig)
|
|||||||
return PRProcessor.Process(pr)
|
return PRProcessor.Process(pr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessIssue(issue *models.Issue, configs []*common.AutogitConfig) error {
|
|
||||||
title := issue.Title
|
|
||||||
/*
|
|
||||||
org := issue.Repository.Owner
|
|
||||||
repo := issue.Repository.Name
|
|
||||||
idx := issue.Index
|
|
||||||
*/
|
|
||||||
const BranchPrefix = "refs/heads/"
|
|
||||||
branch := issue.Ref
|
|
||||||
|
|
||||||
if strings.HasPrefix(branch, BranchPrefix) {
|
|
||||||
branch = strings.TrimPrefix(branch, BranchPrefix)
|
|
||||||
} else {
|
|
||||||
common.LogDebug("Invalid branch specified:", branch, ". Using default.")
|
|
||||||
branch = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// out, _ := json.MarshalIndent(issue, "", " ")
|
|
||||||
// common.LogDebug(string(out))
|
|
||||||
common.LogDebug("issue processing:", common.IssueToString(issue), "@", branch)
|
|
||||||
|
|
||||||
if len(title) > 5 && strings.EqualFold(title[0:5], "[ADD]") {
|
|
||||||
// we need "New Package" label and "Approval Required" label, unless already approved
|
|
||||||
// either via Label "Approved" or via review comment.
|
|
||||||
NewIssues := common.FindNewReposInIssueBody(issue.Body)
|
|
||||||
if NewIssues == nil {
|
|
||||||
common.LogDebug("No new repos found in issue body")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
org := issue.Repository.Owner
|
|
||||||
repo := issue.Repository.Name
|
|
||||||
|
|
||||||
config := common.AutogitConfigs(configs).GetPrjGitConfig(org, repo, branch)
|
|
||||||
if config == nil {
|
|
||||||
return fmt.Errorf("Cannot find config for %s/%s#%s", org, repo, branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(branch) == 0 {
|
|
||||||
branch = config.Branch
|
|
||||||
}
|
|
||||||
|
|
||||||
git, err := GitHandler.CreateGitHandler(config.Organization)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer git.Close()
|
|
||||||
|
|
||||||
for _, nr := range NewIssues.Repos {
|
|
||||||
common.LogDebug(" - Processing new repository src:", nr.Organization+"/"+nr.PackageName+"#"+nr.Branch)
|
|
||||||
targetRepo, err := Gitea.GetRepository(config.Organization, nr.PackageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if targetRepo == nil {
|
|
||||||
common.LogInfo(" - Repository", config.Organization+"/"+nr.PackageName, "does not exist. Labeling issue.")
|
|
||||||
if !common.IsDryRun {
|
|
||||||
Gitea.SetLabels(org, repo, issue.Index, []string{config.Label(common.Label_NewRepository)})
|
|
||||||
}
|
|
||||||
common.LogDebug(" # Done for now with this repo")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
srcRepo, err := Gitea.GetRepository(nr.Organization, nr.Repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if srcRepo == nil {
|
|
||||||
common.LogError("Source repository not found:", nr.Organization+"/"+nr.Repository)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if srcRepo.Parent == nil {
|
|
||||||
common.LogError("Source has no parents.")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nr.Branch) == 0 {
|
|
||||||
nr.Branch = srcRepo.DefaultBranch
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteName, err := git.GitClone(nr.PackageName, nr.Branch, targetRepo.SSHURL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that fork/parent repository relationship exists
|
|
||||||
if srcRepo.Parent.Name != targetRepo.Name || srcRepo.Parent.Owner.UserName != targetRepo.Owner.UserName {
|
|
||||||
common.LogError("Source repository is not fork of the Target repository. Fork of:", srcRepo.Parent.Owner.UserName+"/"+srcRepo.Parent.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
srcBranch := nr.Branch
|
|
||||||
if srcBranch == "" {
|
|
||||||
srcBranch = srcRepo.DefaultBranch
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are ready to setup a pending PR.
|
|
||||||
// 1. empty target branch with empty commit, this will be discarded no merge
|
|
||||||
// 2. create PR from source to target
|
|
||||||
// a) if source is not branch, create a source branch in target repo that contains the relevant commit
|
|
||||||
CreateEmptyBranch := true
|
|
||||||
if err := git.GitExec(nr.PackageName, "checkout", "-B", nr.Branch, remoteName+"/"+nr.Branch); err == nil {
|
|
||||||
// verify that our branch is empty
|
|
||||||
dir, err := git.GitDirectoryContentList(nr.PackageName, "HEAD")
|
|
||||||
if err != nil {
|
|
||||||
common.LogError("Failed to list directory of", nr.Branch, "Aborting", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(dir) == 0 {
|
|
||||||
common.LogDebug("New package branch is already empty.")
|
|
||||||
CreateEmptyBranch = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if CreateEmptyBranch {
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "checkout", "--detach")
|
|
||||||
git.GitExec(nr.PackageName, "branch", "-D", nr.Branch)
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "checkout", "-f", "--orphan", nr.Branch)
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "rm", "-rf", ".")
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "commit", "--allow-empty", "-m", "Initial empty branch")
|
|
||||||
if !common.IsDryRun {
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "push", "-f", remoteName, nr.Branch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
head := nr.Organization + ":" + srcBranch
|
|
||||||
isBranch := false
|
|
||||||
// Hash can be branch name! Check if it's a branch or tag on the remote
|
|
||||||
out, err := git.GitExecWithOutput(nr.PackageName, "ls-remote", "--heads", srcRepo.SSHURL, srcBranch)
|
|
||||||
if err == nil && strings.Contains(out, "refs/heads/"+srcBranch) {
|
|
||||||
isBranch = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isBranch {
|
|
||||||
tempBranch := fmt.Sprintf("new_package_%d_%s", issue.Index, nr.PackageName)
|
|
||||||
// Re-clone or use existing if branch check was done above
|
|
||||||
remoteName, err := git.GitClone(nr.PackageName, srcBranch, targetRepo.SSHURL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "remote", "add", "source", srcRepo.SSHURL)
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "fetch", "source", srcBranch)
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "checkout", "-B", tempBranch, "FETCH_HEAD")
|
|
||||||
if !common.IsDryRun {
|
|
||||||
git.GitExecOrPanic(nr.PackageName, "push", "-f", remoteName, tempBranch)
|
|
||||||
}
|
|
||||||
head = tempBranch
|
|
||||||
}
|
|
||||||
|
|
||||||
title := fmt.Sprintf("Add package %s", nr.PackageName)
|
|
||||||
body := fmt.Sprintf("See issue %s/%s#%d", org, repo, issue.Index)
|
|
||||||
br := branch
|
|
||||||
if len(br) == 0 {
|
|
||||||
br = targetRepo.DefaultBranch
|
|
||||||
}
|
|
||||||
pr, err, isNew := Gitea.CreatePullRequestIfNotExist(targetRepo, head, br, title, body)
|
|
||||||
if err != nil {
|
|
||||||
common.LogError(targetRepo.Name, head, branch, title, body)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if isNew {
|
|
||||||
if _, err := Gitea.SetLabels(config.Organization, nr.PackageName, pr.Index, []string{config.Label(common.Label_NewRepository)}); err != nil {
|
|
||||||
common.LogError("Failed to set label:", common.Label_NewRepository, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if len(title) > 4 && strings.EqualFold(title[0:4], "[RM]") {
|
|
||||||
// to remove a package, no approval is required. This should happen via
|
|
||||||
// project git PR reviews
|
|
||||||
} else {
|
|
||||||
common.LogError("Non-standard issue created. Ignoring", common.IssueToString(issue))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
|
func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -847,18 +654,6 @@ func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
|
|||||||
common.LogError("Cannot find PR for issue:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
|
common.LogError("Cannot find PR for issue:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if req, ok := request.Data.(*common.IssueWebhookEvent); ok {
|
|
||||||
issue, err := Gitea.GetIssue(req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
|
|
||||||
if err != nil {
|
|
||||||
common.LogError("Cannot find issue for issue event:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
configs, ok := w.configuredRepos[req.Repository.Owner.Username]
|
|
||||||
if !ok {
|
|
||||||
common.LogError("*** Cannot find config for org:", req.Repository.Owner.Username)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ProcessIssue(issue, configs)
|
|
||||||
} else {
|
} else {
|
||||||
common.LogError("*** Invalid data format for PR processing.")
|
common.LogError("*** Invalid data format for PR processing.")
|
||||||
return fmt.Errorf("*** Invalid data format for PR processing.")
|
return fmt.Errorf("*** Invalid data format for PR processing.")
|
||||||
|
|||||||
@@ -1,381 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProcessIssue_Add(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
common.IsDryRun = false
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "target-org",
|
|
||||||
GitProjectName: "test-org/test-prj#main",
|
|
||||||
}
|
|
||||||
configs := []*common.AutogitConfig{config}
|
|
||||||
|
|
||||||
issue := &models.Issue{
|
|
||||||
Title: "[ADD] pkg1",
|
|
||||||
Body: "src-org/pkg1#master",
|
|
||||||
Index: 123,
|
|
||||||
Repository: &models.RepositoryMeta{
|
|
||||||
Owner: "test-org",
|
|
||||||
Name: "test-prj",
|
|
||||||
},
|
|
||||||
Ref: "refs/heads/main",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Repository does not exist - labels issue", func(t *testing.T) {
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(nil, nil)
|
|
||||||
gitea.EXPECT().SetLabels("test-org", "test-prj", int64(123), []string{"new/New Repository"}).Return(nil, nil)
|
|
||||||
|
|
||||||
err := ProcessIssue(issue, configs)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Source is SHA - creates temp branch in target", func(t *testing.T) {
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp/test").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
targetRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
SSHURL: "target-ssh-url",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
}
|
|
||||||
srcRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
SSHURL: "src-ssh-url",
|
|
||||||
DefaultBranch: "master",
|
|
||||||
Owner: &models.User{UserName: "src-org"},
|
|
||||||
Parent: &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
issueSHA := &models.Issue{
|
|
||||||
Title: "[ADD] pkg1",
|
|
||||||
Body: "src-org/pkg1#abcdef0123456789abcdef0123456789abcdef01",
|
|
||||||
Index: 123,
|
|
||||||
Repository: &models.RepositoryMeta{Owner: "test-org", Name: "test-prj"},
|
|
||||||
Ref: "refs/heads/main",
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
|
|
||||||
mockGit.EXPECT().GitClone("pkg1", "abcdef0123456789abcdef0123456789abcdef01", "target-ssh-url").Return("origin", nil)
|
|
||||||
|
|
||||||
// New orphan branch logic
|
|
||||||
mockGit.EXPECT().GitExec("pkg1", "checkout", "-B", "abcdef0123456789abcdef0123456789abcdef01", "origin/abcdef0123456789abcdef0123456789abcdef01").Return(fmt.Errorf("error"))
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "--detach")
|
|
||||||
mockGit.EXPECT().GitExec("pkg1", "branch", "-D", "abcdef0123456789abcdef0123456789abcdef01")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-f", "--orphan", "abcdef0123456789abcdef0123456789abcdef01")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "rm", "-rf", ".")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "commit", "--allow-empty", "-m", "Initial empty branch")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "abcdef0123456789abcdef0123456789abcdef01")
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
|
|
||||||
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "abcdef0123456789abcdef0123456789abcdef01").Return("", nil)
|
|
||||||
|
|
||||||
// SHA source logic
|
|
||||||
tempBranch := "new_package_123_pkg1"
|
|
||||||
mockGit.EXPECT().GitClone("pkg1", "abcdef0123456789abcdef0123456789abcdef01", "target-ssh-url").Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "remote", "add", "source", "src-ssh-url")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "fetch", "source", "abcdef0123456789abcdef0123456789abcdef01")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-B", tempBranch, "FETCH_HEAD")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", tempBranch)
|
|
||||||
|
|
||||||
// PR creation using temp branch
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, tempBranch, "main", gomock.Any(), gomock.Any()).Return(&models.PullRequest{Index: 456}, nil, false)
|
|
||||||
|
|
||||||
err := ProcessIssue(issueSHA, configs)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Repository exists - continue processing and create PR", func(t *testing.T) {
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
targetRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
SSHURL: "target-ssh-url",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
}
|
|
||||||
srcRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
SSHURL: "src-ssh-url",
|
|
||||||
DefaultBranch: "master",
|
|
||||||
Owner: &models.User{UserName: "src-org"},
|
|
||||||
Parent: &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
|
|
||||||
// 4. Branch check - exists via local git
|
|
||||||
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExec("pkg1", "checkout", "-B", "master", "origin/master").Return(nil)
|
|
||||||
mockGit.EXPECT().GitDirectoryContentList("pkg1", "HEAD").Return(map[string]string{"some-file": "some-sha"}, nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "--detach")
|
|
||||||
mockGit.EXPECT().GitExec("pkg1", "branch", "-D", "master")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-f", "--orphan", "master")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "rm", "-rf", ".")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "commit", "--allow-empty", "-m", gomock.Any())
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "master")
|
|
||||||
|
|
||||||
// 5. Source repo fetch
|
|
||||||
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
|
|
||||||
// Check if source is a branch via ls-remote
|
|
||||||
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("src-sha refs/heads/master", nil)
|
|
||||||
|
|
||||||
// PR creation
|
|
||||||
packagePR := &models.PullRequest{
|
|
||||||
Index: 456,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: targetRepo,
|
|
||||||
Name: "main",
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Ref: "master",
|
|
||||||
Repo: srcRepo,
|
|
||||||
Sha: "src-sha",
|
|
||||||
},
|
|
||||||
URL: "http://gitea/pr/456",
|
|
||||||
}
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(packagePR, nil, true)
|
|
||||||
gitea.EXPECT().SetLabels("target-org", "pkg1", int64(456), []string{"new/New Repository"}).Return(nil, nil)
|
|
||||||
|
|
||||||
err := ProcessIssue(issue, configs)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Source repository is not fork of target repository - aborts", func(t *testing.T) {
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
targetRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
SSHURL: "target-ssh-url",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
}
|
|
||||||
srcRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "src-org"},
|
|
||||||
Parent: &models.Repository{
|
|
||||||
Name: "other-repo",
|
|
||||||
Owner: &models.User{UserName: "other-org"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
|
|
||||||
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
|
|
||||||
|
|
||||||
err := ProcessIssue(issue, configs)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Source repository is fork of target repository - proceeds", func(t *testing.T) {
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
targetRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
SSHURL: "target-ssh-url",
|
|
||||||
}
|
|
||||||
srcRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "src-org"},
|
|
||||||
SSHURL: "src-ssh-url",
|
|
||||||
Parent: &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
},
|
|
||||||
DefaultBranch: "master",
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
|
|
||||||
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExec("pkg1", "checkout", "-B", "master", "origin/master").Return(nil)
|
|
||||||
mockGit.EXPECT().GitDirectoryContentList("pkg1", "HEAD").Return(map[string]string{"some-file": "some-sha"}, nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "--detach")
|
|
||||||
mockGit.EXPECT().GitExec("pkg1", "branch", "-D", "master")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-f", "--orphan", "master")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "rm", "-rf", ".")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "commit", "--allow-empty", "-m", gomock.Any())
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "master")
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
|
|
||||||
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("src-sha refs/heads/master", nil)
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(&models.PullRequest{Index: 456}, nil, false)
|
|
||||||
|
|
||||||
err := ProcessIssue(issue, configs)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Source repository has no parent (not a fork) - aborts", func(t *testing.T) {
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
targetRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
SSHURL: "target-ssh-url",
|
|
||||||
}
|
|
||||||
srcRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "src-org"},
|
|
||||||
SSHURL: "src-ssh-url",
|
|
||||||
Parent: nil,
|
|
||||||
DefaultBranch: "master",
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
|
|
||||||
|
|
||||||
err := ProcessIssue(issue, configs)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Target branch missing - creates orphan branch", func(t *testing.T) {
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("target-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp/test").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
targetRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
SSHURL: "target-ssh-url",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
}
|
|
||||||
srcRepo := &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
SSHURL: "src-ssh-url",
|
|
||||||
DefaultBranch: "master",
|
|
||||||
Owner: &models.User{UserName: "src-org"},
|
|
||||||
Parent: &models.Repository{
|
|
||||||
Name: "pkg1",
|
|
||||||
Owner: &models.User{UserName: "target-org"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository("target-org", "pkg1").Return(targetRepo, nil)
|
|
||||||
// 4. Branch check - missing
|
|
||||||
mockGit.EXPECT().GitClone("pkg1", "master", "target-ssh-url").Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("not found"))
|
|
||||||
|
|
||||||
// Orphan branch creation
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "--detach")
|
|
||||||
mockGit.EXPECT().GitExec("pkg1", "branch", "-D", "master")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "checkout", "-f", "--orphan", "master")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "rm", "-rf", ".")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "commit", "--allow-empty", "-m", "Initial empty branch")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic("pkg1", "push", "-f", "origin", "master")
|
|
||||||
|
|
||||||
// 5. Source repo fetch
|
|
||||||
gitea.EXPECT().GetRepository("src-org", "pkg1").Return(srcRepo, nil)
|
|
||||||
mockGit.EXPECT().GitExecWithOutput("pkg1", "ls-remote", "--heads", "src-ssh-url", "master").Return("src-sha refs/heads/master", nil)
|
|
||||||
|
|
||||||
// PR creation
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(targetRepo, "src-org:master", "main", gomock.Any(), gomock.Any()).Return(&models.PullRequest{Index: 456}, nil, false)
|
|
||||||
|
|
||||||
err := ProcessIssue(issue, configs)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Config not found", func(t *testing.T) {
|
|
||||||
issueNoConfig := &models.Issue{
|
|
||||||
Title: "[ADD] pkg1",
|
|
||||||
Body: "src-org/pkg1#master",
|
|
||||||
Index: 123,
|
|
||||||
Repository: &models.RepositoryMeta{
|
|
||||||
Owner: "other-org",
|
|
||||||
Name: "other-prj",
|
|
||||||
},
|
|
||||||
Ref: "refs/heads/main",
|
|
||||||
}
|
|
||||||
err := ProcessIssue(issueNoConfig, configs)
|
|
||||||
if err == nil || err.Error() != "Cannot find config for other-org/other-prj#main" {
|
|
||||||
t.Errorf("Expected config not found error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("No repos in body", func(t *testing.T) {
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
err := ProcessIssue(&models.Issue{
|
|
||||||
Title: "[ADD] pkg1",
|
|
||||||
Body: "nothing here",
|
|
||||||
Ref: "refs/heads/main",
|
|
||||||
Repository: &models.RepositoryMeta{
|
|
||||||
Owner: "test-org",
|
|
||||||
Name: "test-prj",
|
|
||||||
},
|
|
||||||
}, configs)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
|
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
mock_common "src.opensuse.org/autogits/common/mock"
|
||||||
)
|
)
|
||||||
@@ -16,7 +17,7 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Reviewers: []string{"reviewer1", "reviewer2"},
|
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "test",
|
Organization: "test",
|
||||||
GitProjectName: "prj#testing",
|
GitProjectName: "prj",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +26,6 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Number: 1,
|
Number: 1,
|
||||||
Pull_Request: &common.PullRequest{
|
Pull_Request: &common.PullRequest{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Number: 1,
|
|
||||||
Base: common.Head{
|
Base: common.Head{
|
||||||
Ref: "branch",
|
Ref: "branch",
|
||||||
Sha: "testing",
|
Sha: "testing",
|
||||||
@@ -53,56 +53,6 @@ func TestOpenPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
modelPR := &models.PullRequest{
|
|
||||||
ID: 1,
|
|
||||||
Index: 1,
|
|
||||||
State: "open",
|
|
||||||
User: &models.User{UserName: "testuser"},
|
|
||||||
RequestedReviewers: []*models.User{},
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "branch",
|
|
||||||
Sha: "testing",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "testRepo",
|
|
||||||
Owner: &models.User{
|
|
||||||
UserName: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Ref: "branch",
|
|
||||||
Sha: "testing",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "testRepo",
|
|
||||||
Owner: &models.User{
|
|
||||||
UserName: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockCreatePR := &models.PullRequest{
|
|
||||||
ID: 2,
|
|
||||||
Index: 2,
|
|
||||||
Body: "Forwarded PRs: testRepo\n\nPR: test/testRepo!1",
|
|
||||||
User: &models.User{UserName: "testuser"},
|
|
||||||
RequestedReviewers: []*models.User{},
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "testing",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "prjcopy",
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Sha: "head",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentUser = &models.User{
|
|
||||||
UserName: "testuser",
|
|
||||||
}
|
|
||||||
|
|
||||||
git := &common.GitHandlerImpl{
|
git := &common.GitHandlerImpl{
|
||||||
GitCommiter: "tester",
|
GitCommiter: "tester",
|
||||||
GitEmail: "test@suse.com",
|
GitEmail: "test@suse.com",
|
||||||
@@ -110,46 +60,14 @@ func TestOpenPR(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("PR git opened request against PrjGit == no action", func(t *testing.T) {
|
t.Run("PR git opened request against PrjGit == no action", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
Gitea = mock_common.NewMockGitea(ctl)
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
pr.config.GitProjectName = "testRepo#testing"
|
pr.config.GitProjectName = "testRepo"
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
pr.git = mockGit
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
if err := pr.Process(event); err != nil {
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
|
|
||||||
if err := pr.Process(modelPR); err != nil {
|
|
||||||
t.Error("Error PrjGit opened request. Should be no error.", err)
|
t.Error("Error PrjGit opened request. Should be no error.", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -161,47 +79,39 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Gitea = gitea
|
Gitea = gitea
|
||||||
|
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
pr.config.GitProjectName = "prjcopy#testing"
|
pr.config.GitProjectName = "prjcopy"
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
prjgit := &models.Repository{
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
SSHURL: "./prj",
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
DefaultBranch: "testing",
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
}
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
giteaPR := &models.PullRequest{
|
||||||
|
Base: &models.PRBranchInfo{
|
||||||
|
Repo: &models.Repository{
|
||||||
|
Owner: &models.User{
|
||||||
|
UserName: "test",
|
||||||
|
},
|
||||||
|
Name: "testRepo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
User: &models.User{
|
||||||
|
UserName: "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// 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().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)
|
||||||
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
err := pr.Process(event)
|
||||||
pr.git = mockGit
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := pr.Process(modelPR)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("error:", err)
|
t.Error("error:", err)
|
||||||
}
|
}
|
||||||
@@ -214,44 +124,15 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Gitea = gitea
|
Gitea = gitea
|
||||||
|
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
pr.config.GitProjectName = "prjcopy#testing"
|
pr.config.GitProjectName = "prjcopy"
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
failedErr := errors.New("Returned error here")
|
failedErr := errors.New("Returned error here")
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr).AnyTimes()
|
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(nil, failedErr)
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
err := pr.Process(event)
|
||||||
pr.git = mockGit
|
if err != failedErr {
|
||||||
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := pr.Process(modelPR)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("error:", err)
|
t.Error("error:", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -262,7 +143,7 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Gitea = gitea
|
Gitea = gitea
|
||||||
|
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
pr.config.GitProjectName = "prjcopy#testing"
|
pr.config.GitProjectName = "prjcopy"
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
@@ -271,37 +152,10 @@ func TestOpenPR(t *testing.T) {
|
|||||||
DefaultBranch: "testing",
|
DefaultBranch: "testing",
|
||||||
}
|
}
|
||||||
failedErr := errors.New("Returned error here")
|
failedErr := errors.New("Returned error here")
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjgit, nil).AnyTimes()
|
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), 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, false)
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
err := pr.Process(event)
|
||||||
pr.git = mockGit
|
|
||||||
|
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := pr.Process(modelPR)
|
|
||||||
if err != failedErr {
|
if err != failedErr {
|
||||||
t.Error("error:", err)
|
t.Error("error:", err)
|
||||||
}
|
}
|
||||||
@@ -313,49 +167,40 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Gitea = gitea
|
Gitea = gitea
|
||||||
|
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
pr.config.GitProjectName = "prjcopy#testing"
|
pr.config.GitProjectName = "prjcopy"
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
|
prjgit := &models.Repository{
|
||||||
|
Name: "SomeRepo",
|
||||||
|
Owner: &models.User{
|
||||||
|
UserName: "org",
|
||||||
|
},
|
||||||
|
SSHURL: "./prj",
|
||||||
|
DefaultBranch: "testing",
|
||||||
|
}
|
||||||
|
giteaPR := &models.PullRequest{
|
||||||
|
Base: &models.PRBranchInfo{
|
||||||
|
Repo: prjgit,
|
||||||
|
},
|
||||||
|
Index: 13,
|
||||||
|
User: &models.User{
|
||||||
|
UserName: "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
failedErr := errors.New("Returned error here")
|
failedErr := errors.New("Returned error here")
|
||||||
|
// gitea.EXPECT().GetAssociatedPrjGitPR("test", "prjcopy", "test", "testRepo", int64(1)).Return(nil, nil)
|
||||||
|
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().RequestReviews(giteaPR, "reviewer1", "reviewer2").Return(nil, failedErr)
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, failedErr).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
err := pr.Process(event)
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
if errors.Unwrap(err) != failedErr {
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
pr.git = mockGit
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := pr.Process(modelPR)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("error:", err)
|
t.Error("error:", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
/*
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
@@ -11,144 +16,217 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestSyncPR(t *testing.T) {
|
func TestSyncPR(t *testing.T) {
|
||||||
CurrentUser = &models.User{UserName: "testuser"}
|
pr := PRProcessor{
|
||||||
config := &common.AutogitConfig{
|
config: &common.AutogitConfig{
|
||||||
Reviewers: []string{"reviewer1", "reviewer2"},
|
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||||
Branch: "testing",
|
Branch: "testing",
|
||||||
Organization: "test-org",
|
Organization: "test",
|
||||||
GitProjectName: "test-prj#testing",
|
GitProjectName: "prj",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
git := &common.GitHandlerImpl{
|
event := &common.PullRequestWebhookEvent{
|
||||||
GitCommiter: "tester",
|
Action: "syncronized",
|
||||||
GitEmail: "test@suse.com",
|
Number: 42,
|
||||||
GitPath: t.TempDir(),
|
Pull_Request: &common.PullRequest{
|
||||||
}
|
Number: 42,
|
||||||
|
Base: common.Head{
|
||||||
processor := &PRProcessor{
|
Ref: "branch",
|
||||||
config: config,
|
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
|
||||||
git: git,
|
Repo: &common.Repository{
|
||||||
|
Name: "testRepo",
|
||||||
|
Owner: &common.Organization{
|
||||||
|
Username: pr.config.Organization,
|
||||||
|
},
|
||||||
|
Default_Branch: "main1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Head: common.Head{
|
||||||
|
Ref: "branch",
|
||||||
|
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
|
||||||
|
Repo: &common.Repository{
|
||||||
|
Name: "testRepo",
|
||||||
|
Default_Branch: "main1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Repository: &common.Repository{
|
||||||
|
Owner: &common.Organization{
|
||||||
|
Username: pr.config.Organization,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
modelPR := &models.PullRequest{
|
modelPR := &models.PullRequest{
|
||||||
Index: 42,
|
Index: 42,
|
||||||
Body: "PR: test-org/test-prj#24",
|
Body: "PR: test/prj#24",
|
||||||
Base: &models.PRBranchInfo{
|
Base: &models.PRBranchInfo{
|
||||||
Ref: "main",
|
Ref: "branch",
|
||||||
|
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
Name: "test-repo",
|
Name: "testRepo",
|
||||||
Owner: &models.User{UserName: "test-org"},
|
Owner: &models.User{
|
||||||
DefaultBranch: "main",
|
UserName: "test",
|
||||||
|
},
|
||||||
|
DefaultBranch: "main1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Head: &models.PRBranchInfo{
|
Head: &models.PRBranchInfo{
|
||||||
Ref: "branch",
|
Ref: "branch",
|
||||||
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
|
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
Name: "test-repo",
|
Name: "testRepo",
|
||||||
Owner: &models.User{UserName: "test-org"},
|
Owner: &models.User{
|
||||||
DefaultBranch: "main",
|
UserName: "test",
|
||||||
|
},
|
||||||
|
DefaultBranch: "main1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
PrjGitPR := &models.PullRequest{
|
PrjGitPR := &models.PullRequest{
|
||||||
Title: "some pull request",
|
Title: "some pull request",
|
||||||
Body: "PR: test-org/test-repo#42",
|
Body: "PR: test/testRepo#42",
|
||||||
Index: 24,
|
Index: 24,
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "testing",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
SSHURL: "url",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
Head: &models.PRBranchInfo{
|
||||||
Name: "PR_test-repo#42",
|
Name: "testing",
|
||||||
Sha: "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95",
|
Sha: "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95",
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
SSHURL: "url",
|
SSHURL: "./prj",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("PR_sync_request_against_PrjGit_==_no_action", func(t *testing.T) {
|
git := &common.GitHandlerImpl{
|
||||||
|
GitCommiter: "tester",
|
||||||
|
GitEmail: "test@suse.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("PR sync request against PrjGit == no action", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
defer ctl.Finish()
|
Gitea = mock_common.NewMockGitea(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
// Common expectations for FetchPRSet and downstream checks
|
git.GitPath = t.TempDir()
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
prjGitRepoPR := &models.PullRequest{
|
pr.config.GitProjectName = "testRepo"
|
||||||
Index: 100,
|
event.Repository.Name = "testRepo"
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "testing",
|
if err := pr.Process(event); err != nil {
|
||||||
Repo: &models.Repository{
|
t.Error("Error PrjGit sync request. Should be no error.", err)
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Ref: "branch",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjGitRepoPR, nil).AnyTimes()
|
t.Run("Missing submodule in prjgit", func(t *testing.T) {
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
ctl := gomock.NewController(t)
|
||||||
|
mock := mock_common.NewMockGitea(ctl)
|
||||||
|
|
||||||
if err := processor.Process(prjGitRepoPR); err != nil {
|
pr.gitea = mock
|
||||||
t.Errorf("Expected nil error for PrjGit sync request, got %v", err)
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
|
pr.config.GitProjectName = "prjGit"
|
||||||
|
event.Repository.Name = "testRepo"
|
||||||
|
|
||||||
|
setupGitForTests(t, git)
|
||||||
|
|
||||||
|
oldSha := PrjGitPR.Head.Sha
|
||||||
|
defer func() { PrjGitPR.Head.Sha = oldSha }()
|
||||||
|
PrjGitPR.Head.Sha = "ab8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95"
|
||||||
|
|
||||||
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
|
||||||
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
|
||||||
|
|
||||||
|
err := pr.Process(event)
|
||||||
|
|
||||||
|
if err == nil || err.Error() != "Cannot fetch submodule commit id in prjgit for 'testRepo'" {
|
||||||
|
t.Error("Invalid error received.", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Missing PrjGit PR for the sync", func(t *testing.T) {
|
t.Run("Missing PrjGit PR for the sync", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
defer ctl.Finish()
|
mock := mock_common.NewMockGitea(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
pr.gitea = mock
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
git.GitPath = t.TempDir()
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
pr.config.GitProjectName = "prjGit"
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("not found")).AnyTimes()
|
event.Repository.Name = "tester"
|
||||||
|
|
||||||
err := processor.Process(modelPR)
|
setupGitForTests(t, git)
|
||||||
// It should fail because it can't find the project PR linked in body
|
|
||||||
if err == nil {
|
expectedErr := errors.New("Missing PR should throw error")
|
||||||
t.Errorf("Expected error for missing project PR, got nil")
|
mock.EXPECT().GetPullRequest(config.Organization, "tester", event.Pull_Request.Number).Return(modelPR, expectedErr)
|
||||||
|
|
||||||
|
err := pr.Process(event, git, config)
|
||||||
|
|
||||||
|
if err == nil || errors.Unwrap(err) != expectedErr {
|
||||||
|
t.Error("Invalid error received.", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("PR sync", func(t *testing.T) {
|
t.Run("PR sync", func(t *testing.T) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
w := log.Writer()
|
||||||
|
log.SetOutput(&b)
|
||||||
|
defer log.SetOutput(w)
|
||||||
|
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
defer ctl.Finish()
|
mock := mock_common.NewMockGitea(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
Gitea = mock
|
||||||
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
|
pr.config.GitProjectName = "prjGit"
|
||||||
|
event.Repository.Name = "testRepo"
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(PrjGitPR, nil).AnyTimes()
|
// mock.EXPECT().GetAssociatedPrjGitPR(event).Return(PrjGitPR, nil)
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
// For UpdatePrjGitPR
|
err := pr.Process(event)
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := processor.Process(modelPR)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Error("Invalid error received.", err)
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that we actually created the branch in the prjgit
|
||||||
|
id, ok := git.GitSubmoduleCommitId("prj", "testRepo", "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c")
|
||||||
|
if id != "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985" || !ok {
|
||||||
|
t.Error("Failed creating PR")
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// does nothing on next sync of already synced data -- PR is updated
|
||||||
|
os.RemoveAll(path.Join(git.GitPath, common.DefaultGitPrj))
|
||||||
|
|
||||||
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
|
||||||
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
|
||||||
|
err = pr.Process(event)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Invalid error received.", err)
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that we actually created the branch in the prjgit
|
||||||
|
id, ok = git.GitSubmoduleCommitId("prj", "testRepo", "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c")
|
||||||
|
if id != "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985" || !ok {
|
||||||
|
t.Error("Failed creating PR")
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if id, err := git.GitBranchHead("prj", "PR_testRepo#42"); id != "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c" || err != nil {
|
||||||
|
t.Error("no branch?", err)
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(b.String(), "commitID already match - nothing to do") {
|
||||||
|
// os.CopyFS("/tmp/test", os.DirFS(git.GitPath))
|
||||||
|
t.Log(b.String())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -1,910 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPrjGitDescription(t *testing.T) {
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-a",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 2,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-b",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
GitAuthor = "Bot"
|
|
||||||
title, desc := PrjGitDescription(prset)
|
|
||||||
|
|
||||||
expectedTitle := "Forwarded PRs: pkg-a, pkg-b"
|
|
||||||
if title != expectedTitle {
|
|
||||||
t.Errorf("Expected title %q, got %q", expectedTitle, title)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(desc, "PR: test-org/pkg-a!1") || !strings.Contains(desc, "PR: test-org/pkg-b!2") {
|
|
||||||
t.Errorf("Description missing PR references: %s", desc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAllocatePRProcessor(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
configs := common.AutogitConfigs{
|
|
||||||
{
|
|
||||||
Organization: "test-org",
|
|
||||||
Branch: "main",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp/test")
|
|
||||||
|
|
||||||
processor, err := AllocatePRProcessor(req, configs)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("AllocatePRProcessor failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if processor.config.Organization != "test-org" {
|
|
||||||
t.Errorf("Expected organization test-org, got %s", processor.config.Organization)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAllocatePRProcessor_Failures(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
configs := common.AutogitConfigs{} // Empty configs
|
|
||||||
|
|
||||||
req := &models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Missing config", func(t *testing.T) {
|
|
||||||
processor, err := AllocatePRProcessor(req, configs)
|
|
||||||
if err == nil || err.Error() != "Cannot find config for PR" {
|
|
||||||
t.Errorf("Expected 'Cannot find config for PR' error, got %v", err)
|
|
||||||
}
|
|
||||||
if processor != nil {
|
|
||||||
t.Error("Expected nil processor")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("GitHandler failure", func(t *testing.T) {
|
|
||||||
validConfigs := common.AutogitConfigs{
|
|
||||||
{
|
|
||||||
Organization: "test-org",
|
|
||||||
Branch: "main",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(nil, errors.New("git error"))
|
|
||||||
|
|
||||||
processor, err := AllocatePRProcessor(req, validConfigs)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Error allocating GitHandler") {
|
|
||||||
t.Errorf("Expected GitHandler error, got %v", err)
|
|
||||||
}
|
|
||||||
if processor != nil {
|
|
||||||
t.Error("Expected nil processor")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSubmodulesToMatchPRSet_Failures(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("GitSubmoduleList failure", func(t *testing.T) {
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(nil, errors.New("list error"))
|
|
||||||
err := processor.SetSubmodulesToMatchPRSet(&common.PRSet{})
|
|
||||||
if err == nil || err.Error() != "list error" {
|
|
||||||
t.Errorf("Expected 'list error', got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSubmodulesToMatchPRSet(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-a",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Sha: "new-sha",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "old-sha"}, nil)
|
|
||||||
// Expect submodule update and commit
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitStatus(gomock.Any()).Return([]common.GitStatusData{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := processor.SetSubmodulesToMatchPRSet(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("SetSubmodulesToMatchPRSet failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRebaseAndSkipSubmoduleCommits(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Clean rebase", func(t *testing.T) {
|
|
||||||
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(nil)
|
|
||||||
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Rebase with submodule conflict - skip", func(t *testing.T) {
|
|
||||||
// First rebase fails
|
|
||||||
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(errors.New("conflict"))
|
|
||||||
// Status shows submodule change
|
|
||||||
mockGit.EXPECT().GitStatus(common.DefaultGitPrj).Return([]common.GitStatusData{
|
|
||||||
{SubmoduleChanges: "S..."},
|
|
||||||
}, nil)
|
|
||||||
// Skip called
|
|
||||||
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "--skip").Return(nil)
|
|
||||||
|
|
||||||
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Rebase with real conflict - abort", func(t *testing.T) {
|
|
||||||
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(errors.New("conflict"))
|
|
||||||
// Status shows real change
|
|
||||||
mockGit.EXPECT().GitStatus(common.DefaultGitPrj).Return([]common.GitStatusData{
|
|
||||||
{SubmoduleChanges: "N..."},
|
|
||||||
}, nil)
|
|
||||||
// Abort called
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(common.DefaultGitPrj, "rebase", "--abort").Return()
|
|
||||||
|
|
||||||
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Unexpected conflict in rebase") {
|
|
||||||
t.Errorf("Expected 'Unexpected conflict' error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePrjGitPR(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
CurrentUser = &models.User{UserName: "bot"}
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Only project git in PR", func(t *testing.T) {
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Sha: "sha1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(common.DefaultGitPrj, "fetch", "origin", "sha1")
|
|
||||||
err := processor.UpdatePrjGitPR(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("PR on another remote", func(t *testing.T) {
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
RepoID: 1,
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
SSHURL: "url",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Name: "feature",
|
|
||||||
RepoID: 2, // Different RepoID
|
|
||||||
Sha: "sha1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "other",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "other-pkg",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("remote2", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "remote2", "sha1")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "sha1")
|
|
||||||
|
|
||||||
err := processor.UpdatePrjGitPR(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Standard update with rebase and force push", func(t *testing.T) {
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
BotUser: "bot",
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Mergeable: false, // Triggers rebase
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
RepoID: 1,
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
SSHURL: "url",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Name: "PR_branch",
|
|
||||||
RepoID: 1,
|
|
||||||
Sha: "old-head",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-a",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any())
|
|
||||||
// Rebase expectations
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), "rebase", gomock.Any()).Return(nil)
|
|
||||||
|
|
||||||
// First call returns old-head, second returns new-head to trigger push
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("old-head", nil).Times(1)
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("new-head", nil).Times(1)
|
|
||||||
|
|
||||||
// SetSubmodulesToMatchPRSet expectations
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "old-pkg-sha"}, nil)
|
|
||||||
// Catch all GitExec calls with any number of arguments up to 5
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
// UpdatePullRequest expectation
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := processor.UpdatePrjGitPR(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("isPrTitleSame logic", func(t *testing.T) {
|
|
||||||
longTitle := strings.Repeat("a", 251) + "..."
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
BotUser: "bot",
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Title: longTitle,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
RepoID: 1,
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Name: "PR_branch",
|
|
||||||
RepoID: 1,
|
|
||||||
Sha: "head",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-a",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any())
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "pkg-sha"}, nil)
|
|
||||||
// mockGit.EXPECT().GitExec(...) not called because no push (headCommit == newHeadCommit)
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
err := processor.UpdatePrjGitPR(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreatePRjGitPR_Integration(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Repo: &models.Repository{Name: "pkg-a", Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Create new project PR with label", func(t *testing.T) {
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head-sha", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
prjPR := &models.PullRequest{
|
|
||||||
Index: 10,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
RepoID: 1,
|
|
||||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Sha: "prj-head-sha",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{Owner: &models.User{UserName: "test-org"}}, nil).AnyTimes()
|
|
||||||
// CreatePullRequestIfNotExist returns isNew=true
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil, true).AnyTimes()
|
|
||||||
// Expect SetLabels to be called for new PR
|
|
||||||
gitea.EXPECT().SetLabels("test-org", gomock.Any(), int64(10), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
|
|
||||||
err := processor.CreatePRjGitPR("PR_branch", prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("CreatePRjGitPR failed: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultiPackagePRSet(t *testing.T) {
|
|
||||||
GitAuthor = "Bot" // Ensure non-empty author
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
name := fmt.Sprintf("pkg-%d", i)
|
|
||||||
prset.PRs = append(prset.PRs, &common.PRInfo{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Index: int64(i),
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{Name: name, Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
GitAuthor = "Bot"
|
|
||||||
title, desc := PrjGitDescription(prset)
|
|
||||||
|
|
||||||
// PrjGitDescription generates title like "Forwarded PRs: pkg-1, pkg-2, pkg-3, pkg-4, pkg-5"
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
name := fmt.Sprintf("pkg-%d", i)
|
|
||||||
if !strings.Contains(title, name) {
|
|
||||||
t.Errorf("Title missing package %s: %s", name, title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
ref := fmt.Sprintf("PR: test-org/pkg-%d!%d", i, i)
|
|
||||||
if !strings.Contains(desc, ref) {
|
|
||||||
t.Errorf("Description missing reference %s", ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPRProcessor_Process_EdgeCases(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
CurrentUser = &models.User{UserName: "bot"}
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Merged project PR - update downstream", func(t *testing.T) {
|
|
||||||
prjPR := &models.PullRequest{
|
|
||||||
State: "closed",
|
|
||||||
HasMerged: true,
|
|
||||||
Index: 100,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}, SSHURL: "url"},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Name: "PR_branch"},
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgPR := &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg-a", Owner: &models.User{UserName: "test-org"}}},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
BotUser: "bot",
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{PR: prjPR},
|
|
||||||
{PR: pkgPR},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_ = prset // Suppress unused for now if it's really unused, but it's likely used by common.FetchPRSet internally if we weren't mocking everything
|
|
||||||
|
|
||||||
// Mock expectations for Process setup
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// Mock maintainership file calls
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
// Mock expectations for the merged path
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"pkg-a": "old-sha"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{{SHA: "pkg-sha"}}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// Downstream update expectations
|
|
||||||
gitea.EXPECT().AddComment(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
gitea.EXPECT().ManualMergePR(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
err := processor.Process(pkgPR)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Superfluous PR - close it", func(t *testing.T) {
|
|
||||||
prjPR := &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 100,
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Body: "Forwarded PRs: \n", // No PRs linked
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Name: "PR_branch", Sha: "head-sha"},
|
|
||||||
MergeBase: "base-sha",
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
BotUser: "bot",
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{PR: prjPR},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_ = prset
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{Owner: &models.User{UserName: "test-org"}}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// Mock maintainership file calls
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
// Standard update calls within Process
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head-sha", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
// Diff check for superfluous
|
|
||||||
mockGit.EXPECT().GitDiff(gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil).AnyTimes()
|
|
||||||
|
|
||||||
// Expectations for closing
|
|
||||||
gitea.EXPECT().AddComment(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
err := processor.Process(prjPR)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVerifyRepositoryConfiguration(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
repo := &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &models.User{
|
|
||||||
UserName: "test-user",
|
|
||||||
},
|
|
||||||
AutodetectManualMerge: true,
|
|
||||||
AllowManualMerge: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Config already correct", func(t *testing.T) {
|
|
||||||
err := verifyRepositoryConfiguration(repo)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Config incorrect - trigger update", func(t *testing.T) {
|
|
||||||
repo.AllowManualMerge = false
|
|
||||||
gitea.EXPECT().SetRepoOptions("test-user", "test-repo", true).Return(&models.Repository{}, nil)
|
|
||||||
|
|
||||||
err := verifyRepositoryConfiguration(repo)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Update failure", func(t *testing.T) {
|
|
||||||
repo.AllowManualMerge = false
|
|
||||||
expectedErr := errors.New("update failed")
|
|
||||||
gitea.EXPECT().SetRepoOptions("test-user", "test-repo", true).Return(nil, expectedErr)
|
|
||||||
|
|
||||||
err := verifyRepositoryConfiguration(repo)
|
|
||||||
if err != expectedErr {
|
|
||||||
t.Errorf("Expected %v, got %v", expectedErr, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProcessFunc(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
reqProc := &RequestProcessor{
|
|
||||||
configuredRepos: map[string][]*common.AutogitConfig{
|
|
||||||
"test-org": {config},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
modelPR := &models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
DefaultBranch: "main",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("PullRequestWebhookEvent", func(t *testing.T) {
|
|
||||||
event := &common.PullRequestWebhookEvent{
|
|
||||||
Pull_Request: &common.PullRequest{
|
|
||||||
Number: 1,
|
|
||||||
Base: common.Head{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &common.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &common.Organization{
|
|
||||||
Username: "test-org",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequest("test-org", "test-repo", int64(1)).Return(modelPR, nil).AnyTimes()
|
|
||||||
// AllocatePRProcessor and ProcesPullRequest calls inside
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
// Expect Process calls (mocked via mockGit mostly)
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := reqProc.ProcessFunc(&common.Request{Data: event})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("IssueCommentWebhookEvent", func(t *testing.T) {
|
|
||||||
event := &common.IssueCommentWebhookEvent{
|
|
||||||
Issue: &common.IssueDetail{Number: 1},
|
|
||||||
Repository: &common.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &common.Organization{Username: "test-org"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequest("test-org", "test-repo", int64(1)).Return(modelPR, nil).AnyTimes()
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
err := reqProc.ProcessFunc(&common.Request{Data: event})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Recursion limit", func(t *testing.T) {
|
|
||||||
reqProc.recursive = 3
|
|
||||||
err := reqProc.ProcessFunc(&common.Request{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error on recursion limit, got %v", err)
|
|
||||||
}
|
|
||||||
if reqProc.recursive != 3 {
|
|
||||||
t.Errorf("Expected recursive to be 3, got %d", reqProc.recursive)
|
|
||||||
}
|
|
||||||
reqProc.recursive = 0 // Reset
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Invalid data format", func(t *testing.T) {
|
|
||||||
err := reqProc.ProcessFunc(&common.Request{Data: nil})
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Invalid data format") {
|
|
||||||
t.Errorf("Expected 'Invalid data format' error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
|
"src.opensuse.org/autogits/workflow-pr/interfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DefaultStateChecker struct {
|
type DefaultStateChecker struct {
|
||||||
@@ -17,11 +18,11 @@ type DefaultStateChecker struct {
|
|||||||
checkOnStart bool
|
checkOnStart bool
|
||||||
checkInterval time.Duration
|
checkInterval time.Duration
|
||||||
|
|
||||||
processor PullRequestProcessor
|
processor *RequestProcessor
|
||||||
i StateChecker
|
i interfaces.StateChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateDefaultStateChecker(checkOnStart bool, processor PullRequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker {
|
func CreateDefaultStateChecker(checkOnStart bool, processor *RequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker {
|
||||||
var s = &DefaultStateChecker{
|
var s = &DefaultStateChecker{
|
||||||
checkInterval: interval,
|
checkInterval: interval,
|
||||||
checkOnStart: checkOnStart,
|
checkOnStart: checkOnStart,
|
||||||
@@ -53,7 +54,7 @@ func (s *DefaultStateChecker) ProcessPR(pr *models.PullRequest, config *common.A
|
|||||||
return ProcesPullRequest(pr, common.AutogitConfigs{config})
|
return ProcesPullRequest(pr, common.AutogitConfigs{config})
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrjGitSubmoduleCheck(config *common.AutogitConfig, git common.Git, repo string, submodules map[string]string) (prsToProcess []*PRToProcess, err error) {
|
func PrjGitSubmoduleCheck(config *common.AutogitConfig, git common.Git, repo string, submodules map[string]string) (prsToProcess []*interfaces.PRToProcess, err error) {
|
||||||
nextSubmodule:
|
nextSubmodule:
|
||||||
for sub, commitID := range submodules {
|
for sub, commitID := range submodules {
|
||||||
common.LogDebug(" + checking", sub, commitID)
|
common.LogDebug(" + checking", sub, commitID)
|
||||||
@@ -73,7 +74,7 @@ nextSubmodule:
|
|||||||
|
|
||||||
branch = repo.DefaultBranch
|
branch = repo.DefaultBranch
|
||||||
}
|
}
|
||||||
prsToProcess = append(prsToProcess, &PRToProcess{
|
prsToProcess = append(prsToProcess, &interfaces.PRToProcess{
|
||||||
Org: config.Organization,
|
Org: config.Organization,
|
||||||
Repo: submoduleName,
|
Repo: submoduleName,
|
||||||
Branch: branch,
|
Branch: branch,
|
||||||
@@ -116,7 +117,7 @@ nextSubmodule:
|
|||||||
return prsToProcess, nil
|
return prsToProcess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) ([]*PRToProcess, error) {
|
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) ([]*interfaces.PRToProcess, error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
common.LogError("panic caught")
|
common.LogError("panic caught")
|
||||||
@@ -127,7 +128,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
prsToProcess := []*PRToProcess{}
|
prsToProcess := []*interfaces.PRToProcess{}
|
||||||
|
|
||||||
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
|
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
|
||||||
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
|
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
|
||||||
@@ -147,7 +148,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
|
|||||||
_, err = git.GitClone(prjGitRepo, prjGitBranch, repo.SSHURL)
|
_, err = git.GitClone(prjGitRepo, prjGitBranch, repo.SSHURL)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
|
|
||||||
prsToProcess = append(prsToProcess, &PRToProcess{
|
prsToProcess = append(prsToProcess, &interfaces.PRToProcess{
|
||||||
Org: prjGitOrg,
|
Org: prjGitOrg,
|
||||||
Repo: prjGitRepo,
|
Repo: prjGitRepo,
|
||||||
Branch: prjGitBranch,
|
Branch: prjGitBranch,
|
||||||
@@ -155,8 +156,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
|
|||||||
submodules, err := git.GitSubmoduleList(prjGitRepo, "HEAD")
|
submodules, err := git.GitSubmoduleList(prjGitRepo, "HEAD")
|
||||||
|
|
||||||
// forward any package-gits referred by the project git, but don't go back
|
// forward any package-gits referred by the project git, but don't go back
|
||||||
subPrs, err := PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules)
|
return PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules)
|
||||||
return append(prsToProcess, subPrs...), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStateChecker) CheckRepos() {
|
func (s *DefaultStateChecker) CheckRepos() {
|
||||||
@@ -170,8 +170,7 @@ func (s *DefaultStateChecker) CheckRepos() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
processor := s.processor.(*RequestProcessor)
|
for org, configs := range s.processor.configuredRepos {
|
||||||
for org, configs := range processor.configuredRepos {
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
if s.checkInterval > 0 {
|
if s.checkInterval > 0 {
|
||||||
sleepInterval := (s.checkInterval - s.checkInterval/2) + time.Duration(rand.Int63n(int64(s.checkInterval)))
|
sleepInterval := (s.checkInterval - s.checkInterval/2) + time.Duration(rand.Int63n(int64(s.checkInterval)))
|
||||||
|
|||||||
@@ -1,333 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPrjGitSubmoduleCheck(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
Branch: "main",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Submodule up to date", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{
|
|
||||||
"pkg-a": "sha-1",
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
|
|
||||||
{SHA: "sha-1"},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
|
|
||||||
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Submodule behind branch", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{
|
|
||||||
"pkg-a": "sha-old",
|
|
||||||
}
|
|
||||||
|
|
||||||
// sha-old is the second commit, meaning it's behind the head (sha-new)
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
|
|
||||||
{SHA: "sha-new"},
|
|
||||||
{SHA: "sha-old"},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
|
|
||||||
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Submodule with new commits - advance branch", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{
|
|
||||||
"pkg-a": "sha-very-new",
|
|
||||||
}
|
|
||||||
|
|
||||||
// sha-very-new is NOT in recent commits
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
|
|
||||||
{SHA: "sha-new"},
|
|
||||||
{SHA: "sha-old"},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), gomock.Any()).Return("commit-1\n").AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "remote", gomock.Any(), gomock.Any(), gomock.Any()).Return("https://src.opensuse.org/test-org/pkg-a.git\n").AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
|
|
||||||
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
|
|
||||||
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrjGitSubmoduleCheck_Failures(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
Branch: "main",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("GetRecentCommits failure", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{"pkg-a": "sha-1"}
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return(nil, errors.New("gitea error"))
|
|
||||||
|
|
||||||
_, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Error fetching recent commits") {
|
|
||||||
t.Errorf("Expected gitea error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("SSH translation failure", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{"pkg-a": "sha-new"}
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{{SHA: "sha-old"}}, nil)
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), gomock.Any()).Return("commit-1\n").AnyTimes()
|
|
||||||
// Return invalid URL that cannot be translated to SSH
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "remote", gomock.Any(), gomock.Any(), gomock.Any()).Return("not-a-url").AnyTimes()
|
|
||||||
|
|
||||||
_, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Cannot traslate HTTPS git URL to SSH_URL") {
|
|
||||||
t.Errorf("Expected SSH translation error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPullRequestToEventState(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
state models.StateType
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"open", "opened"},
|
|
||||||
{"closed", "closed"},
|
|
||||||
{"merged", "merged"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
if got := pullRequestToEventState(tt.state); got != tt.expected {
|
|
||||||
t.Errorf("pullRequestToEventState(%v) = %v; want %v", tt.state, got, tt.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultStateChecker_ProcessPR(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
checker := CreateDefaultStateChecker(false, nil, gitea, time.Duration(0))
|
|
||||||
|
|
||||||
pr := &models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
DefaultBranch: "main",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
// Expectations for ProcesPullRequest
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(pr, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := checker.ProcessPR(pr, config)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("ProcessPR failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultStateChecker_VerifyProjectState(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
checker := CreateDefaultStateChecker(false, nil, gitea, 0)
|
|
||||||
|
|
||||||
t.Run("VerifyProjectState success", func(t *testing.T) {
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), "test-org", "test-prj").Return(&models.Repository{SSHURL: "url"}, nil)
|
|
||||||
mockGit.EXPECT().GitClone("test-prj", "main", "url").Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitSubmoduleList("test-prj", "HEAD").Return(map[string]string{"pkg-a": "sha-1"}, nil)
|
|
||||||
|
|
||||||
// PrjGitSubmoduleCheck call inside
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{DefaultBranch: "main"}, nil).AnyTimes()
|
|
||||||
// Return commits where sha-1 is NOT present
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
|
|
||||||
{SHA: "sha-new"},
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// rev-list returns empty string, so no new commits on branch relative to submodule commitID
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), "sha-1").Return("").AnyTimes()
|
|
||||||
// And ensure submodule update is called
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "submodule", "update", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
|
|
||||||
prs, err := checker.VerifyProjectState(config)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("VerifyProjectState failed: %v", err)
|
|
||||||
}
|
|
||||||
// Expect project git + pkg-a
|
|
||||||
if len(prs) != 2 {
|
|
||||||
t.Errorf("Expected 2 PRs to process, got %d", len(prs))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("VerifyProjectState failure - CreateRepository", func(t *testing.T) {
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), "test-org", "test-prj").Return(nil, errors.New("gitea error"))
|
|
||||||
|
|
||||||
_, err := checker.VerifyProjectState(config)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Error fetching or creating") {
|
|
||||||
t.Errorf("Expected gitea error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultStateChecker_CheckRepos(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
reqProc := &RequestProcessor{
|
|
||||||
configuredRepos: map[string][]*common.AutogitConfig{
|
|
||||||
"test-org": {config},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
checker := CreateDefaultStateChecker(false, nil, gitea, 0)
|
|
||||||
checker.processor = reqProc
|
|
||||||
|
|
||||||
t.Run("CheckRepos success with PRs", func(t *testing.T) {
|
|
||||||
// Mock VerifyProjectState results
|
|
||||||
// TODO: fix below
|
|
||||||
// Since we can't easily mock the internal call s.i.VerifyProjectState because s.i is the checker itself
|
|
||||||
// and VerifyProjectState is not a separate interface method in repo_check.go (it is, but used internally).
|
|
||||||
// Actually DefaultStateChecker implements i (StateChecker interface).
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "url"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// GetRecentPullRequests for the project git
|
|
||||||
gitea.EXPECT().GetRecentPullRequests("test-org", "test-prj", "main").Return([]*models.PullRequest{
|
|
||||||
{Index: 1, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}}}},
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// ProcessPR calls for the found PR
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
checker.CheckRepos()
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("CheckRepos failure - GetRecentPullRequests", func(t *testing.T) {
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "url"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRecentPullRequests("test-org", "test-prj", "main").Return(nil, errors.New("gitea error")).AnyTimes()
|
|
||||||
|
|
||||||
checker.CheckRepos()
|
|
||||||
// Should log error and continue (no panic)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
mock_common "src.opensuse.org/autogits/common/mock"
|
||||||
|
mock_main "src.opensuse.org/autogits/workflow-pr/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRepoCheck(t *testing.T) {
|
func TestRepoCheck(t *testing.T) {
|
||||||
@@ -21,15 +22,16 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
t.Run("Consistency Check On Start", func(t *testing.T) {
|
t.Run("Consistency Check On Start", func(t *testing.T) {
|
||||||
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
state := NewMockStateChecker(ctl)
|
state := mock_main.NewMockStateChecker(ctl)
|
||||||
c.i = state
|
c.i = state
|
||||||
state.EXPECT().CheckRepos().Do(func() {
|
state.EXPECT().CheckRepos().Do(func() error {
|
||||||
// only checkOnStart has checkInterval = 0
|
// only checkOnStart has checkInterval = 0
|
||||||
if c.checkInterval != 0 {
|
if c.checkInterval != 0 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.exitCheckLoop = true
|
c.exitCheckLoop = true
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
c.ConsistencyCheckProcess()
|
c.ConsistencyCheckProcess()
|
||||||
@@ -41,11 +43,11 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
t.Run("No consistency Check On Start", func(t *testing.T) {
|
t.Run("No consistency Check On Start", func(t *testing.T) {
|
||||||
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
state := NewMockStateChecker(ctl)
|
state := mock_main.NewMockStateChecker(ctl)
|
||||||
c.i = state
|
c.i = state
|
||||||
|
|
||||||
nCalls := 10
|
nCalls := 10
|
||||||
state.EXPECT().CheckRepos().Do(func() {
|
state.EXPECT().CheckRepos().Do(func() error {
|
||||||
// only checkOnStart has checkInterval = 0
|
// only checkOnStart has checkInterval = 0
|
||||||
if c.checkInterval != 100 {
|
if c.checkInterval != 100 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@@ -55,6 +57,7 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
if nCalls == 0 {
|
if nCalls == 0 {
|
||||||
c.exitCheckLoop = true
|
c.exitCheckLoop = true
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}).Times(nCalls)
|
}).Times(nCalls)
|
||||||
c.checkOnStart = false
|
c.checkOnStart = false
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("CheckRepos() calls CheckProjectState() for each project", func(t *testing.T) {
|
t.Run("CheckRepos() calls CheckProjectState() for each project", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
state := NewMockStateChecker(ctl)
|
state := mock_main.NewMockStateChecker(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
gitea := mock_common.NewMockGitea(ctl)
|
||||||
|
|
||||||
config1 := &common.AutogitConfig{
|
config1 := &common.AutogitConfig{
|
||||||
@@ -94,12 +97,14 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo2_org"][0])
|
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo2_org"][0])
|
||||||
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo3_org"][0])
|
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo3_org"][0])
|
||||||
|
|
||||||
c.CheckRepos()
|
if err := c.CheckRepos(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("CheckRepos errors", func(t *testing.T) {
|
t.Run("CheckRepos errors", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
state := NewMockStateChecker(ctl)
|
state := mock_main.NewMockStateChecker(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
gitea := mock_common.NewMockGitea(ctl)
|
||||||
|
|
||||||
config1 := &common.AutogitConfig{
|
config1 := &common.AutogitConfig{
|
||||||
@@ -120,7 +125,11 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
err := errors.New("test error")
|
err := errors.New("test error")
|
||||||
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo1_org"][0]).Return(nil, err)
|
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo1_org"][0]).Return(nil, err)
|
||||||
|
|
||||||
c.CheckRepos()
|
r := c.CheckRepos()
|
||||||
|
|
||||||
|
if !errors.Is(r, err) {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,11 +177,11 @@ func TestVerifyProjectState(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{
|
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), config1.GitProjectName).Return(&models.Repository{
|
||||||
SSHURL: "./prj",
|
SSHURL: "./prj",
|
||||||
}, nil).AnyTimes()
|
}, nil)
|
||||||
gitea.EXPECT().GetRecentPullRequests(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullRequest{}, nil).AnyTimes()
|
gitea.EXPECT().GetRecentPullRequests(org, "testRepo", "testing")
|
||||||
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{}, nil).AnyTimes()
|
gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())
|
||||||
|
|
||||||
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
||||||
/*
|
/*
|
||||||
@@ -190,6 +199,7 @@ func TestVerifyProjectState(t *testing.T) {
|
|||||||
t.Run("Project state with 1 PRs that doesn't trigger updates", func(t *testing.T) {
|
t.Run("Project state with 1 PRs that doesn't trigger updates", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
gitea := mock_common.NewMockGitea(ctl)
|
||||||
|
process := mock_main.NewMockPullRequestProcessor(ctl)
|
||||||
|
|
||||||
git := &common.GitHandlerImpl{
|
git := &common.GitHandlerImpl{
|
||||||
GitCommiter: "TestCommiter",
|
GitCommiter: "TestCommiter",
|
||||||
@@ -213,11 +223,11 @@ func TestVerifyProjectState(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{
|
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), config1.GitProjectName).Return(&models.Repository{
|
||||||
SSHURL: "./prj",
|
SSHURL: "./prj",
|
||||||
}, nil).AnyTimes()
|
}, nil)
|
||||||
|
|
||||||
gitea.EXPECT().GetRecentPullRequests(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullRequest{
|
gitea.EXPECT().GetRecentPullRequests(org, "testRepo", "testing").Return([]*models.PullRequest{
|
||||||
&models.PullRequest{
|
&models.PullRequest{
|
||||||
ID: 1234,
|
ID: 1234,
|
||||||
URL: "url here",
|
URL: "url here",
|
||||||
@@ -249,16 +259,16 @@ func TestVerifyProjectState(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil).AnyTimes()
|
}, nil)
|
||||||
|
|
||||||
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{}, nil).AnyTimes()
|
gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())
|
||||||
|
|
||||||
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
||||||
/*
|
/*
|
||||||
c.git = &testGit{
|
c.git = &testGit{
|
||||||
git: git,
|
git: git,
|
||||||
}*/
|
}*/
|
||||||
// process.EXPECT().Process(gomock.Any())
|
process.EXPECT().Process(gomock.Any(), gomock.Any(), gomock.Any())
|
||||||
// c.processor.Opened = process
|
// c.processor.Opened = process
|
||||||
|
|
||||||
_, err := c.VerifyProjectState(configs.configuredRepos[org][0])
|
_, err := c.VerifyProjectState(configs.configuredRepos[org][0])
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate mockgen -source=state_checker.go -destination=mock_state_checker.go -typed -package main
|
|
||||||
|
|
||||||
|
|
||||||
type StateChecker interface {
|
|
||||||
VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error)
|
|
||||||
CheckRepos()
|
|
||||||
ConsistencyCheckProcess() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type PullRequestProcessor interface {
|
|
||||||
Process(req *models.PullRequest) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type PRToProcess struct {
|
|
||||||
Org, Repo, Branch string
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
const LocalCMD = "---"
|
|
||||||
|
|
||||||
func gitExecs(t *testing.T, git *common.GitHandlerImpl, cmds [][]string) {
|
|
||||||
for _, cmd := range cmds {
|
|
||||||
if cmd[0] == LocalCMD {
|
|
||||||
command := exec.Command(cmd[2], cmd[3:]...)
|
|
||||||
command.Dir = filepath.Join(git.GitPath, cmd[1])
|
|
||||||
command.Stdin = nil
|
|
||||||
command.Env = append([]string{"GIT_CONFIG_COUNT=1", "GIT_CONFIG_KEY_1=protocol.file.allow", "GIT_CONFIG_VALUE_1=always"}, common.ExtraGitParams...)
|
|
||||||
_, err := command.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(" *** error: %v\n", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
git.GitExecOrPanic(cmd[0], cmd[1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func commandsForPackages(dir, prefix string, startN, endN int) [][]string {
|
|
||||||
commands := make([][]string, (endN-startN+2)*6)
|
|
||||||
|
|
||||||
if dir == "" {
|
|
||||||
dir = "."
|
|
||||||
}
|
|
||||||
cmdIdx := 0
|
|
||||||
for idx := startN; idx <= endN; idx++ {
|
|
||||||
pkgDir := fmt.Sprintf("%s%d", prefix, idx)
|
|
||||||
|
|
||||||
commands[cmdIdx+0] = []string{"", "init", "-q", "--object-format", "sha256", "-b", "testing", pkgDir}
|
|
||||||
commands[cmdIdx+1] = []string{LocalCMD, pkgDir, "/usr/bin/touch", "testFile"}
|
|
||||||
commands[cmdIdx+2] = []string{pkgDir, "add", "testFile"}
|
|
||||||
commands[cmdIdx+3] = []string{pkgDir, "commit", "-m", "added testFile"}
|
|
||||||
commands[cmdIdx+4] = []string{pkgDir, "config", "receive.denyCurrentBranch", "ignore"}
|
|
||||||
commands[cmdIdx+5] = []string{"prj", "submodule", "add", filepath.Join("..", pkgDir), filepath.Join(dir, pkgDir)}
|
|
||||||
|
|
||||||
cmdIdx += 6
|
|
||||||
}
|
|
||||||
|
|
||||||
// add all the submodules to the prj
|
|
||||||
commands[cmdIdx+0] = []string{"prj", "commit", "-a", "-m", "adding subpackages"}
|
|
||||||
|
|
||||||
return commands
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupGitForTests(t *testing.T, git *common.GitHandlerImpl) {
|
|
||||||
common.ExtraGitParams = []string{
|
|
||||||
"TZ=UTC",
|
|
||||||
"GIT_CONFIG_COUNT=1",
|
|
||||||
"GIT_CONFIG_KEY_0=protocol.file.allow",
|
|
||||||
"GIT_CONFIG_VALUE_0=always",
|
|
||||||
|
|
||||||
"GIT_AUTHOR_NAME=testname",
|
|
||||||
"GIT_AUTHOR_EMAIL=test@suse.com",
|
|
||||||
"GIT_AUTHOR_DATE='2005-04-07T22:13:13'",
|
|
||||||
"GIT_COMMITTER_NAME=testname",
|
|
||||||
"GIT_COMMITTER_EMAIL=test@suse.com",
|
|
||||||
"GIT_COMMITTER_DATE='2005-04-07T22:13:13'",
|
|
||||||
}
|
|
||||||
|
|
||||||
gitExecs(t, git, [][]string{
|
|
||||||
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "prj"},
|
|
||||||
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "foo"},
|
|
||||||
{LocalCMD, "foo", "/usr/bin/touch", "file1"},
|
|
||||||
{"foo", "add", "file1"},
|
|
||||||
{"foo", "commit", "-m", "first commit"},
|
|
||||||
{"prj", "config", "receive.denyCurrentBranch", "ignore"},
|
|
||||||
{"prj", "submodule", "init"},
|
|
||||||
{"prj", "submodule", "add", "../foo", "testRepo"},
|
|
||||||
{"prj", "add", ".gitmodules", "testRepo"},
|
|
||||||
{"prj", "commit", "-m", "First instance"},
|
|
||||||
{"prj", "submodule", "deinit", "testRepo"},
|
|
||||||
{LocalCMD, "foo", "/usr/bin/touch", "file2"},
|
|
||||||
{"foo", "add", "file2"},
|
|
||||||
{"foo", "commit", "-m", "added file2"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user