Compare commits
12 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
| 44bd6c23e5 | |||
| 17bfc36801 | |||
| 73d8c3e97f | |||
| 64e5b51488 | |||
| 1fe6bd98d4 | |||
| e859ed1b54 | |||
| 1ad2e1738c | |||
| a6d5d4c79d | |||
| f5d6f50b32 | |||
| 87633e7508 | |||
| 56ce07514a | |||
| 1d95b4cf0f |
@@ -23,6 +23,7 @@ jobs:
|
||||
- run: git checkout FETCH_HEAD
|
||||
- run: go generate -C common
|
||||
- run: go generate -C workflow-pr
|
||||
- run: go generate -C workflow-pr/interfaces
|
||||
- run: git add -N .; git diff
|
||||
- run: |
|
||||
status=$(git status --short)
|
||||
|
||||
@@ -12,6 +12,7 @@ jobs:
|
||||
- run: git checkout FETCH_HEAD
|
||||
- run: go generate -C common
|
||||
- run: go generate -C workflow-pr
|
||||
- run: go generate -C workflow-pr/interfaces
|
||||
- run: |
|
||||
host=${{ gitea.server_url }}
|
||||
host=${host#https://}
|
||||
|
||||
@@ -129,9 +129,6 @@ go build \
|
||||
go build \
|
||||
-C utils/hujson \
|
||||
-buildmode=pie
|
||||
go build \
|
||||
-C utils/maintainer-update \
|
||||
-buildmode=pie
|
||||
go build \
|
||||
-C gitea-events-rabbitmq-publisher \
|
||||
-buildmode=pie
|
||||
@@ -163,7 +160,6 @@ go test -C group-review -v
|
||||
go test -C obs-staging-bot -v
|
||||
go test -C obs-status-service -v
|
||||
go test -C workflow-direct -v
|
||||
go test -C utils/maintainer-update
|
||||
# TODO build fails
|
||||
#go test -C workflow-pr -v
|
||||
|
||||
@@ -183,7 +179,6 @@ install -D -m0755 workflow-direct/workflow-direct
|
||||
install -D -m0644 systemd/workflow-direct@.service %{buildroot}%{_unitdir}/workflow-direct@.service
|
||||
install -D -m0755 workflow-pr/workflow-pr %{buildroot}%{_bindir}/workflow-pr
|
||||
install -D -m0755 utils/hujson/hujson %{buildroot}%{_bindir}/hujson
|
||||
install -D -m0755 utils/maintainer-update/maintainer-update %{buildroot}%{_bindir}/maintainer-update
|
||||
|
||||
%pre gitea-events-rabbitmq-publisher
|
||||
%service_add_pre gitea-events-rabbitmq-publisher.service
|
||||
@@ -290,7 +285,6 @@ install -D -m0755 utils/maintainer-update/maintainer-update
|
||||
%files utils
|
||||
%license COPYING
|
||||
%{_bindir}/hujson
|
||||
%{_bindir}/maintainer-update
|
||||
|
||||
%files workflow-direct
|
||||
%license COPYING
|
||||
|
||||
@@ -54,7 +54,6 @@ type ReviewGroup struct {
|
||||
type QAConfig struct {
|
||||
Name string
|
||||
Origin string
|
||||
Label string // requires this gitea lable to be set or skipped
|
||||
BuildDisableRepos []string // which repos to build disable in the new project
|
||||
}
|
||||
|
||||
@@ -93,7 +92,6 @@ type AutogitConfig struct {
|
||||
NoProjectGitPR bool // do not automatically create project git PRs, just assign reviewers and assume somethign else creates the ProjectGit PR
|
||||
ManualMergeOnly bool // only merge with "Merge OK" comment by Project Maintainers and/or Package Maintainers and/or reviewers
|
||||
ManualMergeProject bool // require merge of ProjectGit PRs with "Merge OK" by ProjectMaintainers and/or reviewers
|
||||
ReviewRequired bool // always require a maintainer review, even if maintainer submits it. Only ignored if no other package or project reviewers
|
||||
}
|
||||
|
||||
type AutogitConfigs []*AutogitConfig
|
||||
@@ -295,9 +293,9 @@ func (config *AutogitConfig) GetRemoteBranch() string {
|
||||
}
|
||||
|
||||
func (config *AutogitConfig) Label(label string) string {
|
||||
if t, found := config.Labels[LabelKey(label)]; found {
|
||||
return t
|
||||
}
|
||||
if t, found := config.Labels[LabelKey(label)]; found {
|
||||
return t
|
||||
}
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
@@ -20,13 +20,10 @@ package common
|
||||
|
||||
const (
|
||||
GiteaTokenEnv = "GITEA_TOKEN"
|
||||
GiteaHostEnv = "GITEA_HOST"
|
||||
ObsUserEnv = "OBS_USER"
|
||||
ObsPasswordEnv = "OBS_PASSWORD"
|
||||
ObsSshkeyEnv = "OBS_SSHKEY"
|
||||
ObsSshkeyFileEnv = "OBS_SSHKEYFILE"
|
||||
ObsApiEnv = "OBS_API"
|
||||
ObsWebEnv = "OBS_WEB"
|
||||
|
||||
DefaultGitPrj = "_ObsPrj"
|
||||
PrjLinksFile = "links.json"
|
||||
|
||||
@@ -76,7 +76,6 @@ type GiteaLabelSettter interface {
|
||||
}
|
||||
|
||||
type GiteaTimelineFetcher interface {
|
||||
ResetTimelineCache(org, repo string, idx int64)
|
||||
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
|
||||
}
|
||||
|
||||
@@ -814,18 +813,6 @@ type TimelineCacheData struct {
|
||||
var giteaTimelineCache map[string]TimelineCacheData = make(map[string]TimelineCacheData)
|
||||
var giteaTimelineCacheMutex sync.RWMutex
|
||||
|
||||
func (gitea *GiteaTransport) ResetTimelineCache(org, repo string, idx int64) {
|
||||
giteaTimelineCacheMutex.Lock()
|
||||
defer giteaTimelineCacheMutex.Unlock()
|
||||
|
||||
prID := fmt.Sprintf("%s/%s!%d", org, repo, idx)
|
||||
Cache, IsCached := giteaTimelineCache[prID]
|
||||
if IsCached {
|
||||
Cache.lastCheck = Cache.lastCheck.Add(-time.Hour)
|
||||
giteaTimelineCache[prID] = Cache
|
||||
}
|
||||
}
|
||||
|
||||
// returns timeline in reverse chronological create order
|
||||
func (gitea *GiteaTransport) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
||||
page := int64(1)
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||
@@ -27,15 +25,12 @@ const ProjectFileKey = "_project"
|
||||
type MaintainershipMap struct {
|
||||
Data map[string][]string
|
||||
IsDir bool
|
||||
Config *AutogitConfig
|
||||
FetchPackage func(string) ([]byte, error)
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
func ParseMaintainershipData(data []byte) (*MaintainershipMap, error) {
|
||||
func parseMaintainershipData(data []byte) (*MaintainershipMap, error) {
|
||||
maintainers := &MaintainershipMap{
|
||||
Data: make(map[string][]string),
|
||||
Raw: data,
|
||||
}
|
||||
if err := json.Unmarshal(data, &maintainers.Data); err != nil {
|
||||
return nil, err
|
||||
@@ -44,9 +39,7 @@ func ParseMaintainershipData(data []byte) (*MaintainershipMap, error) {
|
||||
return maintainers, nil
|
||||
}
|
||||
|
||||
func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, config *AutogitConfig) (*MaintainershipMap, error) {
|
||||
org, prjGit, branch := config.GetPrjGit()
|
||||
|
||||
func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, org, prjGit, branch string) (*MaintainershipMap, error) {
|
||||
data, _, err := gitea.FetchMaintainershipDirFile(org, prjGit, branch, ProjectFileKey)
|
||||
dir := true
|
||||
if err != nil || data == nil {
|
||||
@@ -66,9 +59,8 @@ func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, config *Aut
|
||||
}
|
||||
}
|
||||
|
||||
m, err := ParseMaintainershipData(data)
|
||||
m, err := parseMaintainershipData(data)
|
||||
if m != nil {
|
||||
m.Config = config
|
||||
m.IsDir = dir
|
||||
m.FetchPackage = func(pkg string) ([]byte, error) {
|
||||
data, _, err := gitea.FetchMaintainershipDirFile(org, prjGit, branch, pkg)
|
||||
@@ -88,8 +80,6 @@ func (data *MaintainershipMap) ListProjectMaintainers(groups []*ReviewGroup) []s
|
||||
return nil
|
||||
}
|
||||
|
||||
m = slices.Clone(m)
|
||||
|
||||
// expands groups
|
||||
for _, g := range groups {
|
||||
m = g.ExpandMaintainers(m)
|
||||
@@ -126,7 +116,6 @@ func (data *MaintainershipMap) ListPackageMaintainers(pkg string, groups []*Revi
|
||||
}
|
||||
}
|
||||
}
|
||||
pkgMaintainers = slices.Clone(pkgMaintainers)
|
||||
prjMaintainers := data.ListProjectMaintainers(nil)
|
||||
|
||||
prjMaintainer:
|
||||
@@ -160,10 +149,7 @@ func (data *MaintainershipMap) IsApproved(pkg string, reviews []*models.PullRevi
|
||||
}
|
||||
|
||||
LogDebug("Looking for review by:", reviewers)
|
||||
slices.Sort(reviewers)
|
||||
reviewers = slices.Compact(reviewers)
|
||||
SubmitterIdxInReviewers := slices.Index(reviewers, submitter)
|
||||
if SubmitterIdxInReviewers > -1 && (!data.Config.ReviewRequired || len(reviewers) == 1) {
|
||||
if slices.Contains(reviewers, submitter) {
|
||||
LogDebug("Submitter is maintainer. Approving.")
|
||||
return true
|
||||
}
|
||||
@@ -178,135 +164,13 @@ func (data *MaintainershipMap) IsApproved(pkg string, reviews []*models.PullRevi
|
||||
return false
|
||||
}
|
||||
|
||||
func (data *MaintainershipMap) modifyInplace(writer io.StringWriter) error {
|
||||
var original map[string][]string
|
||||
if err := json.Unmarshal(data.Raw, &original); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(data.Raw))
|
||||
_, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output := ""
|
||||
lastPos := 0
|
||||
modified := false
|
||||
|
||||
type entry struct {
|
||||
key string
|
||||
valStart int
|
||||
valEnd int
|
||||
}
|
||||
var entries []entry
|
||||
|
||||
for dec.More() {
|
||||
kToken, _ := dec.Token()
|
||||
key := kToken.(string)
|
||||
var raw json.RawMessage
|
||||
dec.Decode(&raw)
|
||||
valEnd := int(dec.InputOffset())
|
||||
valStart := valEnd - len(raw)
|
||||
entries = append(entries, entry{key, valStart, valEnd})
|
||||
}
|
||||
|
||||
changed := make(map[string]bool)
|
||||
for k, v := range data.Data {
|
||||
if ov, ok := original[k]; !ok || !slices.Equal(v, ov) {
|
||||
changed[k] = true
|
||||
}
|
||||
}
|
||||
for k := range original {
|
||||
if _, ok := data.Data[k]; !ok {
|
||||
changed[k] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(changed) == 0 {
|
||||
_, err = writer.WriteString(string(data.Raw))
|
||||
return err
|
||||
}
|
||||
|
||||
for _, e := range entries {
|
||||
if v, ok := data.Data[e.key]; ok {
|
||||
prefix := string(data.Raw[lastPos:e.valStart])
|
||||
if modified && strings.TrimSpace(output) == "{" {
|
||||
if commaIdx := strings.Index(prefix, ","); commaIdx != -1 {
|
||||
if quoteIdx := strings.Index(prefix, "\""); quoteIdx == -1 || commaIdx < quoteIdx {
|
||||
prefix = prefix[:commaIdx] + prefix[commaIdx+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
output += prefix
|
||||
if changed[e.key] {
|
||||
slices.Sort(v)
|
||||
newVal, _ := json.Marshal(v)
|
||||
output += string(newVal)
|
||||
modified = true
|
||||
} else {
|
||||
output += string(data.Raw[e.valStart:e.valEnd])
|
||||
}
|
||||
} else {
|
||||
// Deleted
|
||||
modified = true
|
||||
}
|
||||
lastPos = e.valEnd
|
||||
}
|
||||
output += string(data.Raw[lastPos:])
|
||||
|
||||
// Handle additions (simplistic: at the end)
|
||||
for k, v := range data.Data {
|
||||
if _, ok := original[k]; !ok {
|
||||
slices.Sort(v)
|
||||
newVal, _ := json.Marshal(v)
|
||||
keyStr, _ := json.Marshal(k)
|
||||
|
||||
// Insert before closing brace
|
||||
if idx := strings.LastIndex(output, "}"); idx != -1 {
|
||||
prefix := output[:idx]
|
||||
suffix := output[idx:]
|
||||
|
||||
trimmedPrefix := strings.TrimRight(prefix, " \n\r\t")
|
||||
if !strings.HasSuffix(trimmedPrefix, "{") && !strings.HasSuffix(trimmedPrefix, ",") {
|
||||
// find the actual position of the last non-whitespace character in prefix
|
||||
lastCharIdx := strings.LastIndexAny(prefix, "]}0123456789\"")
|
||||
if lastCharIdx != -1 {
|
||||
prefix = prefix[:lastCharIdx+1] + "," + prefix[lastCharIdx+1:]
|
||||
}
|
||||
}
|
||||
|
||||
insertion := fmt.Sprintf(" %s: %s", string(keyStr), string(newVal))
|
||||
if !strings.HasSuffix(prefix, "\n") {
|
||||
insertion = "\n" + insertion
|
||||
}
|
||||
output = prefix + insertion + "\n" + suffix
|
||||
modified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if modified {
|
||||
_, err := writer.WriteString(output)
|
||||
return err
|
||||
}
|
||||
_, err = writer.WriteString(string(data.Raw))
|
||||
return err
|
||||
}
|
||||
|
||||
func (data *MaintainershipMap) WriteMaintainershipFile(writer io.StringWriter) error {
|
||||
if data.IsDir {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
if len(data.Raw) > 0 {
|
||||
if err := data.modifyInplace(writer); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to full write
|
||||
writer.WriteString("{\n")
|
||||
|
||||
if d, ok := data.Data[""]; ok {
|
||||
eol := ","
|
||||
if len(data.Data) == 1 {
|
||||
@@ -317,12 +181,17 @@ func (data *MaintainershipMap) WriteMaintainershipFile(writer io.StringWriter) e
|
||||
writer.WriteString(fmt.Sprintf(" \"\": %s%s\n", string(str), eol))
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(data.Data))
|
||||
keys := make([]string, len(data.Data))
|
||||
i := 0
|
||||
for pkg := range data.Data {
|
||||
if pkg == "" {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, pkg)
|
||||
keys[i] = pkg
|
||||
i++
|
||||
}
|
||||
if len(keys) >= i {
|
||||
keys = slices.Delete(keys, i, len(keys))
|
||||
}
|
||||
slices.Sort(keys)
|
||||
for i, pkg := range keys {
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
)
|
||||
|
||||
func TestMaintainership(t *testing.T) {
|
||||
config := &common.AutogitConfig{
|
||||
config := common.AutogitConfig{
|
||||
Branch: "bar",
|
||||
Organization: "foo",
|
||||
GitProjectName: common.DefaultGitPrj + "#bar",
|
||||
GitProjectName: common.DefaultGitPrj,
|
||||
}
|
||||
|
||||
packageTests := []struct {
|
||||
@@ -141,7 +141,7 @@ func TestMaintainership(t *testing.T) {
|
||||
notFoundError := repository.NewRepoGetContentsNotFound()
|
||||
for _, test := range packageTests {
|
||||
runTests := func(t *testing.T, mi common.GiteaMaintainershipReader) {
|
||||
maintainers, err := common.FetchProjectMaintainershipData(mi, config)
|
||||
maintainers, err := common.FetchProjectMaintainershipData(mi, config.Organization, config.GitProjectName, config.Branch)
|
||||
if err != nil && !test.otherError {
|
||||
if test.maintainersFileErr == nil {
|
||||
t.Fatal("Unexpected error recieved", err)
|
||||
@@ -208,7 +208,6 @@ func TestMaintainershipFileWrite(t *testing.T) {
|
||||
name string
|
||||
is_dir bool
|
||||
maintainers map[string][]string
|
||||
raw []byte
|
||||
expected_output string
|
||||
expected_error error
|
||||
}{
|
||||
@@ -232,43 +231,6 @@ func TestMaintainershipFileWrite(t *testing.T) {
|
||||
},
|
||||
expected_output: "{\n \"\": [\"one\",\"two\"],\n \"foo\": [\"byte\",\"four\"],\n \"pkg1\": []\n}\n",
|
||||
},
|
||||
{
|
||||
name: "surgical modification",
|
||||
maintainers: map[string][]string{
|
||||
"": {"one", "two"},
|
||||
"foo": {"byte", "four", "newone"},
|
||||
"pkg1": {},
|
||||
},
|
||||
raw: []byte("{\n \"\": [\"one\",\"two\"],\n \"foo\": [\"byte\",\"four\"],\n \"pkg1\": []\n}\n"),
|
||||
expected_output: "{\n \"\": [\"one\",\"two\"],\n \"foo\": [\"byte\",\"four\",\"newone\"],\n \"pkg1\": []\n}\n",
|
||||
},
|
||||
{
|
||||
name: "no change",
|
||||
maintainers: map[string][]string{
|
||||
"": {"one", "two"},
|
||||
"foo": {"byte", "four"},
|
||||
"pkg1": {},
|
||||
},
|
||||
raw: []byte("{\n \"\": [\"one\",\"two\"],\n \"foo\": [\"byte\",\"four\"],\n \"pkg1\": []\n}\n"),
|
||||
expected_output: "{\n \"\": [\"one\",\"two\"],\n \"foo\": [\"byte\",\"four\"],\n \"pkg1\": []\n}\n",
|
||||
},
|
||||
{
|
||||
name: "surgical addition",
|
||||
maintainers: map[string][]string{
|
||||
"": {"one"},
|
||||
"new": {"user"},
|
||||
},
|
||||
raw: []byte("{\n \"\": [ \"one\" ]\n}\n"),
|
||||
expected_output: "{\n \"\": [ \"one\" ],\n \"new\": [\"user\"]\n}\n",
|
||||
},
|
||||
{
|
||||
name: "surgical deletion",
|
||||
maintainers: map[string][]string{
|
||||
"": {"one"},
|
||||
},
|
||||
raw: []byte("{\n \"\": [\"one\"],\n \"old\": [\"user\"]\n}\n"),
|
||||
expected_output: "{\n \"\": [\"one\"]\n}\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -277,7 +239,6 @@ func TestMaintainershipFileWrite(t *testing.T) {
|
||||
data := common.MaintainershipMap{
|
||||
Data: test.maintainers,
|
||||
IsDir: test.is_dir,
|
||||
Raw: test.raw,
|
||||
}
|
||||
|
||||
if err := data.WriteMaintainershipFile(&b); err != test.expected_error {
|
||||
@@ -287,134 +248,8 @@ func TestMaintainershipFileWrite(t *testing.T) {
|
||||
output := b.String()
|
||||
|
||||
if test.expected_output != output {
|
||||
t.Fatalf("unexpected output:\n%q\nExpecting:\n%q", output, test.expected_output)
|
||||
t.Fatal("unexpected output:", output, "Expecting:", test.expected_output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReviewRequired(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
maintainers []string
|
||||
config *common.AutogitConfig
|
||||
is_approved bool
|
||||
}{
|
||||
{
|
||||
name: "ReviewRequired=false",
|
||||
maintainers: []string{"maintainer1", "maintainer2"},
|
||||
config: &common.AutogitConfig{ReviewRequired: false},
|
||||
is_approved: true,
|
||||
},
|
||||
{
|
||||
name: "ReviewRequired=true",
|
||||
maintainers: []string{"maintainer1", "maintainer2"},
|
||||
config: &common.AutogitConfig{ReviewRequired: true},
|
||||
is_approved: false,
|
||||
},
|
||||
{
|
||||
name: "ReviewRequired=true",
|
||||
maintainers: []string{"maintainer1"},
|
||||
config: &common.AutogitConfig{ReviewRequired: true},
|
||||
is_approved: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
m := &common.MaintainershipMap{
|
||||
Data: map[string][]string{"": test.maintainers},
|
||||
}
|
||||
m.Config = test.config
|
||||
if approved := m.IsApproved("", nil, "maintainer1", nil); approved != test.is_approved {
|
||||
t.Error("Expected m.IsApproved()->", test.is_approved, "but didn't get it")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaintainershipDataCorruption_PackageAppend(t *testing.T) {
|
||||
// Test corruption when append happens (merging project maintainers)
|
||||
// If backing array has capacity, append writes to it.
|
||||
|
||||
// We construct a slice with capacity > len to simulate this common scenario
|
||||
backingArray := make([]string, 1, 10)
|
||||
backingArray[0] = "@g1"
|
||||
|
||||
initialData := map[string][]string{
|
||||
"pkg": backingArray, // len 1, cap 10
|
||||
"": {"prjUser"},
|
||||
}
|
||||
|
||||
m := &common.MaintainershipMap{
|
||||
Data: initialData,
|
||||
}
|
||||
|
||||
groups := []*common.ReviewGroup{
|
||||
{
|
||||
Name: "@g1",
|
||||
Reviewers: []string{"u1"},
|
||||
},
|
||||
}
|
||||
|
||||
// ListPackageMaintainers("pkg", groups)
|
||||
// 1. gets ["@g1"] (cap 10)
|
||||
// 2. Appends "prjUser" -> ["@g1", "prjUser"] (in backing array)
|
||||
// 3. Expands "@g1" -> "u1".
|
||||
// Replace: ["u1", "prjUser"]
|
||||
// Sort: ["prjUser", "u1"]
|
||||
//
|
||||
// The backing array is now ["prjUser", "u1", ...]
|
||||
// The map entry "pkg" is still len 1.
|
||||
// So it sees ["prjUser"].
|
||||
|
||||
list1 := m.ListPackageMaintainers("pkg", groups)
|
||||
t.Logf("List1: %v", list1)
|
||||
|
||||
// ListPackageMaintainers("pkg", nil)
|
||||
// Should be ["@g1", "prjUser"] (because prjUser is appended from project maintainers)
|
||||
// But since backing array is corrupted:
|
||||
// It sees ["prjUser"] (from map) + appends "prjUser" -> ["prjUser", "prjUser"].
|
||||
|
||||
list2 := m.ListPackageMaintainers("pkg", nil)
|
||||
t.Logf("List2: %v", list2)
|
||||
|
||||
if !slices.Contains(list2, "@g1") {
|
||||
t.Errorf("Corruption: '@g1' is missing from second call. Got %v", list2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaintainershipDataCorruption_ProjectInPlace(t *testing.T) {
|
||||
// Test corruption in ListProjectMaintainers when replacement fits in place
|
||||
// e.g. replacing 1 group with 1 user.
|
||||
|
||||
initialData := map[string][]string{
|
||||
"": {"@g1"},
|
||||
}
|
||||
|
||||
m := &common.MaintainershipMap{
|
||||
Data: initialData,
|
||||
}
|
||||
|
||||
groups := []*common.ReviewGroup{
|
||||
{
|
||||
Name: "@g1",
|
||||
Reviewers: []string{"u1"},
|
||||
},
|
||||
}
|
||||
|
||||
// First call with expansion
|
||||
// Replaces "@g1" with "u1". Length stays 1. Modifies backing array in place.
|
||||
list1 := m.ListProjectMaintainers(groups)
|
||||
t.Logf("List1: %v", list1)
|
||||
|
||||
// Second call without expansion
|
||||
// Should return ["@g1"]
|
||||
list2 := m.ListProjectMaintainers(nil)
|
||||
t.Logf("List2: %v", list2)
|
||||
|
||||
if !slices.Contains(list2, "@g1") {
|
||||
t.Errorf("Corruption: '@g1' is missing from second call (Project). Got %v", list2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -207,42 +207,6 @@ func (c *MockGiteaTimelineFetcherGetTimelineCall) DoAndReturn(f func(string, str
|
||||
return c
|
||||
}
|
||||
|
||||
// ResetTimelineCache mocks base method.
|
||||
func (m *MockGiteaTimelineFetcher) ResetTimelineCache(org, repo string, idx int64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
|
||||
}
|
||||
|
||||
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
|
||||
func (mr *MockGiteaTimelineFetcherMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaTimelineFetcherResetTimelineCacheCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaTimelineFetcher)(nil).ResetTimelineCache), org, repo, idx)
|
||||
return &MockGiteaTimelineFetcherResetTimelineCacheCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaTimelineFetcherResetTimelineCacheCall wrap *gomock.Call
|
||||
type MockGiteaTimelineFetcherResetTimelineCacheCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaTimelineFetcherResetTimelineCacheCall) Return() *MockGiteaTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.Return()
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaTimelineFetcherResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaTimelineFetcherResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// MockGiteaComment is a mock of GiteaComment interface.
|
||||
type MockGiteaComment struct {
|
||||
ctrl *gomock.Controller
|
||||
@@ -739,42 +703,6 @@ func (c *MockGiteaPRTimelineReviewFetcherGetTimelineCall) DoAndReturn(f func(str
|
||||
return c
|
||||
}
|
||||
|
||||
// ResetTimelineCache mocks base method.
|
||||
func (m *MockGiteaPRTimelineReviewFetcher) ResetTimelineCache(org, repo string, idx int64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
|
||||
}
|
||||
|
||||
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
|
||||
func (mr *MockGiteaPRTimelineReviewFetcherMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaPRTimelineReviewFetcher)(nil).ResetTimelineCache), org, repo, idx)
|
||||
return &MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall wrap *gomock.Call
|
||||
type MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall) Return() *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.Return()
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaPRTimelineReviewFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// MockGiteaCommitFetcher is a mock of GiteaCommitFetcher interface.
|
||||
type MockGiteaCommitFetcher struct {
|
||||
ctrl *gomock.Controller
|
||||
@@ -1066,42 +994,6 @@ func (c *MockGiteaReviewTimelineFetcherGetTimelineCall) DoAndReturn(f func(strin
|
||||
return c
|
||||
}
|
||||
|
||||
// ResetTimelineCache mocks base method.
|
||||
func (m *MockGiteaReviewTimelineFetcher) ResetTimelineCache(org, repo string, idx int64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
|
||||
}
|
||||
|
||||
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
|
||||
func (mr *MockGiteaReviewTimelineFetcherMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaReviewTimelineFetcherResetTimelineCacheCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaReviewTimelineFetcher)(nil).ResetTimelineCache), org, repo, idx)
|
||||
return &MockGiteaReviewTimelineFetcherResetTimelineCacheCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaReviewTimelineFetcherResetTimelineCacheCall wrap *gomock.Call
|
||||
type MockGiteaReviewTimelineFetcherResetTimelineCacheCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaReviewTimelineFetcherResetTimelineCacheCall) Return() *MockGiteaReviewTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.Return()
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaReviewTimelineFetcherResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaReviewTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaReviewTimelineFetcherResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaReviewTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// MockGiteaPRChecker is a mock of GiteaPRChecker interface.
|
||||
type MockGiteaPRChecker struct {
|
||||
ctrl *gomock.Controller
|
||||
@@ -1323,42 +1215,6 @@ func (c *MockGiteaPRCheckerGetTimelineCall) DoAndReturn(f func(string, string, i
|
||||
return c
|
||||
}
|
||||
|
||||
// ResetTimelineCache mocks base method.
|
||||
func (m *MockGiteaPRChecker) ResetTimelineCache(org, repo string, idx int64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
|
||||
}
|
||||
|
||||
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
|
||||
func (mr *MockGiteaPRCheckerMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaPRCheckerResetTimelineCacheCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaPRChecker)(nil).ResetTimelineCache), org, repo, idx)
|
||||
return &MockGiteaPRCheckerResetTimelineCacheCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaPRCheckerResetTimelineCacheCall wrap *gomock.Call
|
||||
type MockGiteaPRCheckerResetTimelineCacheCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaPRCheckerResetTimelineCacheCall) Return() *MockGiteaPRCheckerResetTimelineCacheCall {
|
||||
c.Call = c.Call.Return()
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaPRCheckerResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaPRCheckerResetTimelineCacheCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaPRCheckerResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaPRCheckerResetTimelineCacheCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// MockGiteaReviewFetcherAndRequesterAndUnrequester is a mock of GiteaReviewFetcherAndRequesterAndUnrequester interface.
|
||||
type MockGiteaReviewFetcherAndRequesterAndUnrequester struct {
|
||||
ctrl *gomock.Controller
|
||||
@@ -1544,42 +1400,6 @@ func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall) DoA
|
||||
return c
|
||||
}
|
||||
|
||||
// ResetTimelineCache mocks base method.
|
||||
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) ResetTimelineCache(org, repo string, idx int64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
|
||||
}
|
||||
|
||||
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
|
||||
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).ResetTimelineCache), org, repo, idx)
|
||||
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall wrap *gomock.Call
|
||||
type MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall) Return() *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall {
|
||||
c.Call = c.Call.Return()
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterResetTimelineCacheCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// UnrequestReview mocks base method.
|
||||
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) UnrequestReview(org, repo string, id int64, reviwers ...string) error {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1686,42 +1506,6 @@ func (c *MockGiteaUnreviewTimelineFetcherGetTimelineCall) DoAndReturn(f func(str
|
||||
return c
|
||||
}
|
||||
|
||||
// ResetTimelineCache mocks base method.
|
||||
func (m *MockGiteaUnreviewTimelineFetcher) ResetTimelineCache(org, repo string, idx int64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
|
||||
}
|
||||
|
||||
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
|
||||
func (mr *MockGiteaUnreviewTimelineFetcherMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGiteaUnreviewTimelineFetcher)(nil).ResetTimelineCache), org, repo, idx)
|
||||
return &MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall wrap *gomock.Call
|
||||
type MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall) Return() *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.Return()
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaUnreviewTimelineFetcherResetTimelineCacheCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// UnrequestReview mocks base method.
|
||||
func (m *MockGiteaUnreviewTimelineFetcher) UnrequestReview(org, repo string, id int64, reviwers ...string) error {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -3324,42 +3108,6 @@ func (c *MockGiteaRequestReviewsCall) DoAndReturn(f func(*models.PullRequest, ..
|
||||
return c
|
||||
}
|
||||
|
||||
// ResetTimelineCache mocks base method.
|
||||
func (m *MockGitea) ResetTimelineCache(org, repo string, idx int64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "ResetTimelineCache", org, repo, idx)
|
||||
}
|
||||
|
||||
// ResetTimelineCache indicates an expected call of ResetTimelineCache.
|
||||
func (mr *MockGiteaMockRecorder) ResetTimelineCache(org, repo, idx any) *MockGiteaResetTimelineCacheCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetTimelineCache", reflect.TypeOf((*MockGitea)(nil).ResetTimelineCache), org, repo, idx)
|
||||
return &MockGiteaResetTimelineCacheCall{Call: call}
|
||||
}
|
||||
|
||||
// MockGiteaResetTimelineCacheCall wrap *gomock.Call
|
||||
type MockGiteaResetTimelineCacheCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockGiteaResetTimelineCacheCall) Return() *MockGiteaResetTimelineCacheCall {
|
||||
c.Call = c.Call.Return()
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockGiteaResetTimelineCacheCall) Do(f func(string, string, int64)) *MockGiteaResetTimelineCacheCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockGiteaResetTimelineCacheCall) DoAndReturn(f func(string, string, int64)) *MockGiteaResetTimelineCacheCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetCommitStatus mocks base method.
|
||||
func (m *MockGitea) SetCommitStatus(org, repo, hash string, status *models.CommitStatus) (*models.CommitStatus, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -116,18 +116,13 @@ type Flags struct {
|
||||
Contents string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
type ProjectLinkMeta struct {
|
||||
Project string `xml:"project,attr"`
|
||||
}
|
||||
|
||||
type ProjectMeta struct {
|
||||
XMLName xml.Name `xml:"project"`
|
||||
Name string `xml:"name,attr"`
|
||||
Title string `xml:"title"`
|
||||
Description string `xml:"description"`
|
||||
Url string `xml:"url,omitempty"`
|
||||
ScmSync string `xml:"scmsync,omitempty"`
|
||||
Link []ProjectLinkMeta `xml:"link"`
|
||||
ScmSync string `xml:"scmsync"`
|
||||
Persons []PersonRepoMeta `xml:"person"`
|
||||
Groups []GroupRepoMeta `xml:"group"`
|
||||
Repositories []RepositoryMeta `xml:"repository"`
|
||||
@@ -143,8 +138,8 @@ type ProjectMeta struct {
|
||||
type PackageMeta struct {
|
||||
XMLName xml.Name `xml:"package"`
|
||||
Name string `xml:"name,attr"`
|
||||
Project string `xml:"project,attr,omitempty"`
|
||||
ScmSync string `xml:"scmsync,omitempty"`
|
||||
Project string `xml:"project,attr"`
|
||||
ScmSync string `xml:"scmsync"`
|
||||
Persons []PersonRepoMeta `xml:"person"`
|
||||
Groups []GroupRepoMeta `xml:"group"`
|
||||
|
||||
|
||||
13
common/pr.go
13
common/pr.go
@@ -160,8 +160,6 @@ func FetchPRSet(user string, gitea GiteaPRTimelineReviewFetcher, org, repo strin
|
||||
var pr *models.PullRequest
|
||||
var err error
|
||||
|
||||
gitea.ResetTimelineCache(org, repo, num)
|
||||
|
||||
prjGitOrg, prjGitRepo, _ := config.GetPrjGit()
|
||||
if prjGitOrg == org && prjGitRepo == repo {
|
||||
if pr, err = gitea.GetPullRequest(org, repo, num); err != nil {
|
||||
@@ -186,7 +184,6 @@ func FetchPRSet(user string, gitea GiteaPRTimelineReviewFetcher, org, repo strin
|
||||
|
||||
for _, pr := range prs {
|
||||
org, repo, idx := pr.PRComponents()
|
||||
gitea.ResetTimelineCache(org, repo, idx)
|
||||
reviews, err := FetchGiteaReviews(gitea, org, repo, idx)
|
||||
if err != nil {
|
||||
LogError("Error fetching reviews for", PRtoString(pr.PR), ":", err)
|
||||
@@ -319,10 +316,7 @@ func (rs *PRSet) FindMissingAndExtraReviewers(maintainers MaintainershipData, id
|
||||
// only need project maintainer reviews if:
|
||||
// * not created by a bot and has other PRs, or
|
||||
// * not created by maintainer
|
||||
noReviewPRCreators := []string{}
|
||||
if !rs.Config.ReviewRequired {
|
||||
noReviewPRCreators = prjMaintainers
|
||||
}
|
||||
noReviewPRCreators := prjMaintainers
|
||||
if len(rs.PRs) > 1 {
|
||||
noReviewPRCreators = append(noReviewPRCreators, rs.BotUser)
|
||||
}
|
||||
@@ -345,10 +339,7 @@ func (rs *PRSet) FindMissingAndExtraReviewers(maintainers MaintainershipData, id
|
||||
pkg := pr.PR.Base.Repo.Name
|
||||
pkgMaintainers := maintainers.ListPackageMaintainers(pkg, nil)
|
||||
Maintainers := slices.Concat(prjMaintainers, pkgMaintainers)
|
||||
noReviewPkgPRCreators := []string{}
|
||||
if !rs.Config.ReviewRequired {
|
||||
noReviewPkgPRCreators = pkgMaintainers
|
||||
}
|
||||
noReviewPkgPRCreators := pkgMaintainers
|
||||
|
||||
LogDebug("packakge maintainers:", Maintainers)
|
||||
|
||||
|
||||
@@ -833,6 +833,7 @@ func TestFindMissingAndExtraReviewers(t *testing.T) {
|
||||
[]string{"autogits_obs_staging_bot", "user1"},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "Add reviewer if also maintainer where review by maintainer is not needed",
|
||||
prset: &common.PRSet{
|
||||
@@ -1094,67 +1095,8 @@ func TestFindMissingAndExtraReviewers(t *testing.T) {
|
||||
expected_missing_reviewers: [][]string{{"pkgm2", "prj2"}},
|
||||
expected_extra_reviewers: [][]string{{}, {"prj1"}},
|
||||
},
|
||||
{
|
||||
name: "Package maintainer submitter, AlwaysRequireReview=false",
|
||||
prset: &common.PRSet{
|
||||
PRs: []*common.PRInfo{
|
||||
{
|
||||
PR: &models.PullRequest{
|
||||
User: &models.User{UserName: "pkgmaintainer"},
|
||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
||||
},
|
||||
Reviews: &common.PRReviews{},
|
||||
},
|
||||
},
|
||||
Config: &common.AutogitConfig{
|
||||
GitProjectName: "prg/repo#main",
|
||||
Organization: "org",
|
||||
Branch: "main",
|
||||
Reviewers: []string{},
|
||||
ReviewRequired: false,
|
||||
},
|
||||
},
|
||||
maintainers: &common.MaintainershipMap{
|
||||
Data: map[string][]string{
|
||||
"pkg": {"pkgmaintainer", "pkgm1"},
|
||||
},
|
||||
},
|
||||
noAutoStaging: true,
|
||||
expected_missing_reviewers: [][]string{
|
||||
{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Package maintainer submitter, AlwaysRequireReview=true",
|
||||
prset: &common.PRSet{
|
||||
PRs: []*common.PRInfo{
|
||||
{
|
||||
PR: &models.PullRequest{
|
||||
User: &models.User{UserName: "pkgmaintainer"},
|
||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
||||
},
|
||||
Reviews: &common.PRReviews{},
|
||||
},
|
||||
},
|
||||
Config: &common.AutogitConfig{
|
||||
GitProjectName: "prg/repo#main",
|
||||
Organization: "org",
|
||||
Branch: "main",
|
||||
Reviewers: []string{},
|
||||
ReviewRequired: true,
|
||||
},
|
||||
},
|
||||
maintainers: &common.MaintainershipMap{
|
||||
Data: map[string][]string{
|
||||
"pkg": {"pkgmaintainer", "pkgm1"},
|
||||
},
|
||||
},
|
||||
noAutoStaging: true,
|
||||
expected_missing_reviewers: [][]string{
|
||||
{"pkgm1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.prset.HasAutoStaging = !test.noAutoStaging
|
||||
|
||||
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
|
||||
Kernel:firmware
|
||||
Kernel:kdump
|
||||
@@ -11,7 +12,6 @@ devel:languages:lua
|
||||
devel:languages:nodejs
|
||||
devel:languages:perl
|
||||
devel:languages:python:Factory
|
||||
devel:languages:python:mailman
|
||||
devel:languages:python:pytest
|
||||
devel:openSUSE:Factory
|
||||
network:chromium
|
||||
|
||||
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 = '';
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@ It's a JSON file with following syntax:
|
||||
{
|
||||
"Name": "SLES",
|
||||
"Origin": "SUSE:SLFO:Products:SLES:16.0",
|
||||
"Label": "BootstrapRing",
|
||||
"BuildDisableRepos": ["product"]
|
||||
}
|
||||
]
|
||||
@@ -49,7 +48,6 @@ It's a JSON file with following syntax:
|
||||
| *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 > Label* | Setup the project only when the given gitea label is set on pull request | no | string | | |
|
||||
| *QA > BuildDisableRepos* | The names of OBS repositories to build-disable, if any. | no | array of strings | | [] |
|
||||
|
||||
|
||||
@@ -71,10 +69,4 @@ Details
|
||||
* 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.
|
||||
* QA projects can build on each other. In this case it is important that the order to setup is correct
|
||||
in the staging.config file.
|
||||
* Based on Label settings QA projects can get created or removed. The staging bot is also checking that these
|
||||
projects build successfully.
|
||||
* It is possible to include the sources from the staging project also in the QA project. Define a template using
|
||||
a project link pointing to the project defined as "StagingProject". You must *not* use scmsync directly in the
|
||||
same project then, but you can use it indirectly via a second project link
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ package main
|
||||
*/
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"flag"
|
||||
@@ -115,105 +114,161 @@ type DisableFlag struct {
|
||||
Name string `xml:"repository,attr"`
|
||||
}
|
||||
|
||||
func ProcessBuildStatus(project *common.BuildResultList) BuildStatusSummary {
|
||||
func ProcessBuildStatus(project, refProject *common.BuildResultList) BuildStatusSummary {
|
||||
if _, finished := refProject.BuildResultSummary(); !finished {
|
||||
common.LogDebug("refProject not finished building??")
|
||||
return BuildStatusSummaryUnknown
|
||||
}
|
||||
|
||||
if _, finished := project.BuildResultSummary(); !finished {
|
||||
common.LogDebug("Still building...")
|
||||
return BuildStatusSummaryBuilding
|
||||
}
|
||||
|
||||
common.LogDebug("build results", len(project.Result))
|
||||
// the repositories should be setup equally between the projects. We
|
||||
// need to verify that packages that are building in `refProject` are not
|
||||
// failing in the `project`
|
||||
BuildResultSorter := func(a, b *common.BuildResult) int {
|
||||
if c := strings.Compare(a.Repository, b.Repository); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := strings.Compare(a.Arch, b.Arch); c != 0 {
|
||||
return c
|
||||
}
|
||||
|
||||
panic("Should not happen -- BuiltResultSorter equal repos?")
|
||||
}
|
||||
slices.SortFunc(project.Result, BuildResultSorter)
|
||||
if refProject == nil {
|
||||
// just return if buid finished and have some successes, since new package
|
||||
common.LogInfo("New package. Only need some success...")
|
||||
SomeSuccess := false
|
||||
for i := 0; i < len(project.Result); i++ {
|
||||
repoRes := project.Result[i]
|
||||
repoResStatus, ok := common.ObsRepoStatusDetails[repoRes.Code]
|
||||
if !ok {
|
||||
common.LogDebug("cannot find code:", repoRes.Code)
|
||||
return BuildStatusSummaryUnknown
|
||||
}
|
||||
if !repoResStatus.Finished {
|
||||
return BuildStatusSummaryBuilding
|
||||
}
|
||||
|
||||
for _, pkg := range repoRes.Status {
|
||||
pkgStatus, ok := common.ObsBuildStatusDetails[pkg.Code]
|
||||
if !ok {
|
||||
common.LogInfo("Unknown package build status:", pkg.Code, "for", pkg.Package)
|
||||
common.LogDebug("Details:", pkg.Details)
|
||||
}
|
||||
|
||||
if pkgStatus.Success {
|
||||
SomeSuccess = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if SomeSuccess {
|
||||
return BuildStatusSummarySuccess
|
||||
}
|
||||
return BuildStatusSummaryFailed
|
||||
}
|
||||
|
||||
slices.SortFunc(refProject.Result, BuildResultSorter)
|
||||
|
||||
common.LogDebug("comparing results", len(project.Result), "vs. ref", len(refProject.Result))
|
||||
SomeSuccess := false
|
||||
for i := 0; i < len(project.Result); i++ {
|
||||
common.LogDebug("searching for", project.Result[i].Repository, "/", project.Result[i].Arch)
|
||||
j := 0
|
||||
found:
|
||||
for j := 0; j < len(project.Result); j++ {
|
||||
for ; j < len(refProject.Result); j++ {
|
||||
if project.Result[i].Repository != refProject.Result[j].Repository ||
|
||||
project.Result[i].Arch != refProject.Result[j].Arch {
|
||||
continue
|
||||
}
|
||||
|
||||
common.LogDebug(" found match for @ idx:", j)
|
||||
res := ProcessRepoBuildStatus(project.Result[i].Status)
|
||||
res, success := ProcessRepoBuildStatus(project.Result[i].Status, refProject.Result[j].Status)
|
||||
switch res {
|
||||
case BuildStatusSummarySuccess:
|
||||
SomeSuccess = SomeSuccess || success
|
||||
break found
|
||||
case BuildStatusSummaryFailed:
|
||||
return BuildStatusSummaryFailed
|
||||
default:
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
if j >= len(refProject.Result) {
|
||||
common.LogDebug("Cannot find results...")
|
||||
common.LogDebug(project.Result[i])
|
||||
common.LogDebug(refProject.Result)
|
||||
return BuildStatusSummaryUnknown
|
||||
}
|
||||
}
|
||||
|
||||
return BuildStatusSummarySuccess
|
||||
if SomeSuccess {
|
||||
return BuildStatusSummarySuccess
|
||||
}
|
||||
|
||||
return BuildStatusSummaryFailed
|
||||
}
|
||||
|
||||
func ProcessRepoBuildStatus(results []*common.PackageBuildStatus) (status BuildStatusSummary) {
|
||||
|
||||
PackageBuildStatusSorter := func(a, b *common.PackageBuildStatus) int {
|
||||
return strings.Compare(a.Package, b.Package)
|
||||
}
|
||||
func ProcessRepoBuildStatus(results, ref []*common.PackageBuildStatus) (status BuildStatusSummary, SomeSuccess bool) {
|
||||
PackageBuildStatusSorter := func(a, b *common.PackageBuildStatus) int {
|
||||
return strings.Compare(a.Package, b.Package)
|
||||
}
|
||||
|
||||
common.LogDebug("******** REF: ")
|
||||
data, _ := xml.MarshalIndent(ref, "", " ")
|
||||
common.LogDebug(string(data))
|
||||
common.LogDebug("******* RESULTS: ")
|
||||
data, _ := xml.MarshalIndent(results, "", " ")
|
||||
data, _ = xml.MarshalIndent(results, "", " ")
|
||||
common.LogDebug(string(data))
|
||||
common.LogDebug("*******")
|
||||
|
||||
// compare build result
|
||||
slices.SortFunc(results, PackageBuildStatusSorter)
|
||||
slices.SortFunc(ref, PackageBuildStatusSorter)
|
||||
|
||||
j := 0
|
||||
SomeSuccess = false
|
||||
for i := 0; i < len(results); i++ {
|
||||
res, ok := common.ObsBuildStatusDetails[results[i].Code]
|
||||
if !ok {
|
||||
common.LogInfo("unknown package result code:", results[i].Code, "for package:", results[i].Package)
|
||||
return BuildStatusSummaryUnknown
|
||||
return BuildStatusSummaryUnknown, SomeSuccess
|
||||
}
|
||||
|
||||
if !res.Finished {
|
||||
return BuildStatusSummaryBuilding
|
||||
return BuildStatusSummaryBuilding, SomeSuccess
|
||||
}
|
||||
|
||||
if !res.Success {
|
||||
return BuildStatusSummaryFailed
|
||||
}
|
||||
}
|
||||
|
||||
return BuildStatusSummarySuccess
|
||||
}
|
||||
|
||||
func GetPackageBuildStatus(project *common.BuildResultList, packageName string) (bool, BuildStatusSummary) {
|
||||
var packageStatuses []*common.PackageBuildStatus
|
||||
|
||||
// Collect all statuses for the package
|
||||
for _, result := range project.Result {
|
||||
for _, pkgStatus := range result.Status {
|
||||
if pkgStatus.Package == packageName {
|
||||
packageStatuses = append(packageStatuses, pkgStatus)
|
||||
// not failed if reference project also failed for same package here
|
||||
for ; j < len(results) && strings.Compare(results[i].Package, ref[j].Package) < 0; j++ {
|
||||
}
|
||||
|
||||
if j < len(results) && results[i].Package == ref[j].Package {
|
||||
refRes, ok := common.ObsBuildStatusDetails[ref[j].Code]
|
||||
if !ok {
|
||||
common.LogInfo("unknown ref package result code:", ref[j].Code, "package:", ref[j].Package)
|
||||
return BuildStatusSummaryUnknown, SomeSuccess
|
||||
}
|
||||
|
||||
if !refRes.Finished {
|
||||
common.LogDebug("Not finished building in reference project?")
|
||||
}
|
||||
|
||||
if refRes.Success {
|
||||
return BuildStatusSummaryFailed, SomeSuccess
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SomeSuccess = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(packageStatuses) == 0 {
|
||||
return true, BuildStatusSummaryUnknown // true for 'missing'
|
||||
}
|
||||
|
||||
// Check for any failures
|
||||
for _, pkgStatus := range packageStatuses {
|
||||
res, ok := common.ObsBuildStatusDetails[pkgStatus.Code]
|
||||
if !ok {
|
||||
common.LogInfo("unknown package result code:", pkgStatus.Code, "for package:", pkgStatus.Package)
|
||||
return false, BuildStatusSummaryUnknown
|
||||
}
|
||||
if !res.Success {
|
||||
return false, BuildStatusSummaryFailed
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any unfinished builds
|
||||
for _, pkgStatus := range packageStatuses {
|
||||
res, _ := common.ObsBuildStatusDetails[pkgStatus.Code]
|
||||
// 'ok' is already checked in the loop above
|
||||
if !res.Finished {
|
||||
return false, BuildStatusSummaryBuilding
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, all are finished and successful
|
||||
return false, BuildStatusSummarySuccess
|
||||
return BuildStatusSummarySuccess, SomeSuccess
|
||||
}
|
||||
|
||||
func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingPrj, buildPrj string, stagingMasterPrj string) (*common.ProjectMeta, error) {
|
||||
@@ -272,9 +327,9 @@ func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque
|
||||
common.LogError("error fetching project meta for", buildPrj, ". Err:", err)
|
||||
return nil, err
|
||||
}
|
||||
common.LogInfo("Meta: ", meta)
|
||||
|
||||
// generate new project with paths pointinig back to original repos
|
||||
// disable publishing
|
||||
|
||||
meta.Name = stagingPrj
|
||||
meta.Description = fmt.Sprintf(`Pull request build job PR#%d to branch %s of %s/%s`,
|
||||
@@ -289,10 +344,7 @@ func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque
|
||||
|
||||
urlPkg := make([]string, 0, len(modifiedOrNew))
|
||||
for _, pkg := range modifiedOrNew {
|
||||
// FIXME: skip manifest subdirectories itself
|
||||
// strip any leading directory name and just hand over last directory as package name
|
||||
onlybuilds := strings.Split(pkg, "/")
|
||||
urlPkg = append(urlPkg, "onlybuild="+url.QueryEscape(onlybuilds[len(onlybuilds)-1]))
|
||||
urlPkg = append(urlPkg, "onlybuild="+url.QueryEscape(pkg))
|
||||
}
|
||||
meta.ScmSync = pr.Head.Repo.CloneURL + "?" + strings.Join(urlPkg, "&") + "#" + pr.Head.Sha
|
||||
if len(meta.ScmSync) >= 65535 {
|
||||
@@ -332,14 +384,12 @@ func GenerateObsPrjMeta(git common.Git, gitea common.Gitea, pr *models.PullReque
|
||||
|
||||
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("reading templateProject ", templateProject)
|
||||
templateMeta, err := ObsClient.GetProjectMeta(templateProject)
|
||||
if err != nil {
|
||||
common.LogError("error fetching template project meta for", templateProject, ":", err)
|
||||
return err
|
||||
}
|
||||
// patch baseMeta to become the new project
|
||||
common.LogDebug("upcoming project name ", stagingProject, ":", subProjectName)
|
||||
templateMeta.Name = stagingProject + ":" + subProjectName
|
||||
// freeze tag for now
|
||||
if len(templateMeta.ScmSync) > 0 {
|
||||
@@ -377,50 +427,19 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
|
||||
templateMeta.BuildFlags.Contents += string(output)
|
||||
}
|
||||
}
|
||||
|
||||
// include sources from submission project when link points to staging project
|
||||
for idx, l := range templateMeta.Link {
|
||||
if l.Project == stagingConfig.StagingProject {
|
||||
templateMeta.Link[idx].Project = stagingProject
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup ReleaseTarget and modify affected path entries
|
||||
for idx, r := range templateMeta.Repositories {
|
||||
templateMeta.Repositories[idx].ReleaseTargets = nil
|
||||
|
||||
for pidx, path := range r.Paths {
|
||||
// Check for path building against code stream
|
||||
common.LogDebug(" checking in ", templateMeta.Name)
|
||||
common.LogDebug(" stagingProject ", stagingProject)
|
||||
common.LogDebug(" checking for ", templateMeta.Repositories[idx].Paths[pidx].Project)
|
||||
common.LogDebug(" path.Project ", path.Project)
|
||||
common.LogDebug(" stagingConfig.ObsProject ", stagingConfig.ObsProject)
|
||||
common.LogDebug(" stagingConfig.StagingProject ", stagingConfig.StagingProject)
|
||||
common.LogDebug(" templateProject ", templateProject)
|
||||
if path.Project == stagingConfig.ObsProject {
|
||||
templateMeta.Repositories[idx].Paths[pidx].Project = stagingProject
|
||||
} else
|
||||
}
|
||||
// Check for path building against a repo in template project itself
|
||||
if path.Project == templateProject {
|
||||
templateMeta.Repositories[idx].Paths[pidx].Project = templateMeta.Name
|
||||
} else
|
||||
// Check for path prefixes against a template project inside of template project area
|
||||
if strings.HasPrefix(path.Project, stagingConfig.StagingProject + ":") {
|
||||
newProjectName := stagingProject
|
||||
// find project name
|
||||
for _, setup := range stagingConfig.QA {
|
||||
if setup.Origin == path.Project {
|
||||
common.LogDebug(" Match:", setup.Origin)
|
||||
newProjectName = newProjectName + ":" + setup.Name
|
||||
common.LogDebug(" New:", newProjectName)
|
||||
break
|
||||
}
|
||||
}
|
||||
templateMeta.Repositories[idx].Paths[pidx].Project = newProjectName
|
||||
common.LogDebug(" Matched prefix")
|
||||
}
|
||||
common.LogDebug(" Path using project ", templateMeta.Repositories[idx].Paths[pidx].Project)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,8 +447,6 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
|
||||
err = ObsClient.SetProjectMeta(templateMeta)
|
||||
if err != nil {
|
||||
common.LogError("cannot create project:", templateMeta.Name, err)
|
||||
x, _ := xml.MarshalIndent(templateMeta, "", " ")
|
||||
common.LogError(string(x))
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@@ -536,7 +553,7 @@ func FetchOurLatestActionableReview(gitea common.Gitea, org, repo string, id int
|
||||
}
|
||||
|
||||
func ParseNotificationToPR(thread *models.NotificationThread) (org string, repo string, num int64, err error) {
|
||||
rx := regexp.MustCompile(`^.*/api/v\d+/repos/(?<org>[-_a-zA-Z0-9]+)/(?<project>[-_a-zA-Z0-9]+)/issues/(?<num>[0-9]+)$`)
|
||||
rx := regexp.MustCompile(`^https://src\.(?:open)?suse\.(?:org|de)/api/v\d+/repos/(?<org>[-_a-zA-Z0-9]+)/(?<project>[-_a-zA-Z0-9]+)/issues/(?<num>[0-9]+)$`)
|
||||
notification := thread.Subject
|
||||
match := rx.FindStringSubmatch(notification.URL)
|
||||
if match == nil {
|
||||
@@ -685,64 +702,6 @@ func SetStatus(gitea common.Gitea, org, repo, hash string, status *models.Commit
|
||||
return err
|
||||
}
|
||||
|
||||
func commentOnPackagePR(gitea common.Gitea, org string, repo string, prNum int64, msg string) {
|
||||
if IsDryRun {
|
||||
common.LogInfo("Would comment on package PR %s/%s#%d: %s", org, repo, prNum, msg)
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := gitea.GetPullRequest(org, repo, prNum)
|
||||
if err != nil {
|
||||
common.LogError("Failed to get package PR %s/%s#%d: %v", org, repo, prNum, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = gitea.AddComment(pr, msg)
|
||||
if err != nil {
|
||||
common.LogError("Failed to comment on package PR %s/%s#%d: %v", org, repo, prNum, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create and remove QA projects
|
||||
func ProcessQaProjects(stagingConfig *common.StagingConfig, git common.Git, gitea common.Gitea, pr *models.PullRequest, stagingProject string) []string {
|
||||
usedQAprojects := make([]string, 0)
|
||||
prLabelNames := make(map[string]int)
|
||||
for _, label := range pr.Labels {
|
||||
prLabelNames[label.Name] = 1
|
||||
}
|
||||
msg := ""
|
||||
for _, setup := range stagingConfig.QA {
|
||||
QAproject := stagingProject + ":" + setup.Name
|
||||
if len(setup.Label) > 0 {
|
||||
if _, ok := prLabelNames[setup.Label]; !ok {
|
||||
if !IsDryRun {
|
||||
// blindly remove, will fail when not existing
|
||||
ObsClient.DeleteProject(QAproject)
|
||||
}
|
||||
common.LogInfo("QA project ", setup.Name, "has no matching Label")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
usedQAprojects = append(usedQAprojects, QAproject)
|
||||
// check for existens first, no error, but no meta is a 404
|
||||
if meta, err := ObsClient.GetProjectMeta(QAproject); meta == nil && err == nil {
|
||||
common.LogInfo("Create QA project ", QAproject)
|
||||
CreateQASubProject(stagingConfig, git, gitea, pr,
|
||||
stagingProject,
|
||||
setup.Origin,
|
||||
setup.Name,
|
||||
setup.BuildDisableRepos)
|
||||
msg = msg + "QA Project added: " + ObsWebHost + "/project/show/" +
|
||||
QAproject + "\n"
|
||||
}
|
||||
}
|
||||
if len(msg) > 1 {
|
||||
gitea.AddComment(pr, msg)
|
||||
}
|
||||
return usedQAprojects
|
||||
}
|
||||
|
||||
func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, error) {
|
||||
dir, err := os.MkdirTemp(os.TempDir(), BotName)
|
||||
common.PanicOnError(err)
|
||||
@@ -820,12 +779,11 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
|
||||
meta, err := ObsClient.GetProjectMeta(stagingConfig.ObsProject)
|
||||
if err != nil || meta == nil {
|
||||
common.LogError("Cannot find reference project meta:", stagingConfig.ObsProject, err)
|
||||
if !IsDryRun && err == nil {
|
||||
common.LogError("Reference project is absent:", stagingConfig.ObsProject, err)
|
||||
if !IsDryRun {
|
||||
_, err := gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, "Cannot fetch reference project meta")
|
||||
return true, err
|
||||
}
|
||||
return true, err
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if metaUrl, err := url.Parse(meta.ScmSync); err != nil {
|
||||
@@ -975,49 +933,36 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
|
||||
msg = "Build is started in " + ObsWebHost + "/project/show/" +
|
||||
stagingProject + " .\n"
|
||||
|
||||
if len(stagingConfig.QA) > 0 {
|
||||
msg = msg + "\nAdditional QA builds: \n"
|
||||
}
|
||||
SetStatus(gitea, org, repo, pr.Head.Sha, status)
|
||||
for _, setup := range stagingConfig.QA {
|
||||
CreateQASubProject(stagingConfig, git, gitea, pr,
|
||||
stagingProject,
|
||||
setup.Origin,
|
||||
setup.Name,
|
||||
setup.BuildDisableRepos)
|
||||
msg = msg + ObsWebHost + "/project/show/" +
|
||||
stagingProject + ":" + setup.Name + "\n"
|
||||
}
|
||||
}
|
||||
if change != RequestModificationNoChange && !IsDryRun {
|
||||
gitea.AddComment(pr, msg)
|
||||
}
|
||||
|
||||
baseResult, err := ObsClient.LastBuildResults(stagingConfig.ObsProject, modifiedPackages...)
|
||||
if err != nil {
|
||||
common.LogError("failed fetching ref project status for", stagingConfig.ObsProject, ":", err)
|
||||
}
|
||||
stagingResult, err := ObsClient.BuildStatus(stagingProject)
|
||||
if err != nil {
|
||||
common.LogError("failed fetching stage project status for", stagingProject, ":", err)
|
||||
}
|
||||
|
||||
_, packagePRs := common.ExtractDescriptionAndPRs(bufio.NewScanner(strings.NewReader(pr.Body)))
|
||||
|
||||
// always update QA projects because Labels can change
|
||||
qaProjects := ProcessQaProjects(stagingConfig, git, gitea, pr, stagingProject)
|
||||
buildStatus := ProcessBuildStatus(stagingResult, baseResult)
|
||||
|
||||
done := false
|
||||
overallBuildStatus := ProcessBuildStatus(stagingResult)
|
||||
commentSuffix := ""
|
||||
if len(qaProjects) > 0 && overallBuildStatus == BuildStatusSummarySuccess {
|
||||
seperator := " in "
|
||||
for _, qaProject := range qaProjects {
|
||||
qaResult, err := ObsClient.BuildStatus(qaProject)
|
||||
if err != nil {
|
||||
common.LogError("failed fetching stage project status for", qaProject, ":", err)
|
||||
}
|
||||
qaBuildStatus := ProcessBuildStatus(qaResult)
|
||||
if qaBuildStatus != BuildStatusSummarySuccess {
|
||||
// either still building or in failed state
|
||||
overallBuildStatus = qaBuildStatus
|
||||
commentSuffix = commentSuffix + seperator + qaProject
|
||||
seperator = ", "
|
||||
}
|
||||
if qaBuildStatus == BuildStatusSummaryFailed {
|
||||
// main project was successful, but QA project, adapt the link to QA project
|
||||
// and change commit state to fail
|
||||
status.Status = common.CommitStatus_Fail
|
||||
status.TargetURL = ObsWebHost + "/project/show/" + qaProject
|
||||
SetStatus(gitea, org, repo, pr.Head.Sha, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
switch overallBuildStatus {
|
||||
switch buildStatus {
|
||||
case BuildStatusSummarySuccess:
|
||||
status.Status = common.CommitStatus_Success
|
||||
done = true
|
||||
@@ -1037,44 +982,7 @@ func ProcessPullRequest(gitea common.Gitea, org, repo string, id int64) (bool, e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if overallBuildStatus == BuildStatusSummarySuccess || overallBuildStatus == BuildStatusSummaryFailed {
|
||||
// avoid commenting while build is in progress
|
||||
missingPkgs := []string{}
|
||||
|
||||
for _, packagePR := range packagePRs {
|
||||
missing, packageBuildStatus := GetPackageBuildStatus(stagingResult, packagePR.Repo)
|
||||
if missing {
|
||||
missingPkgs = append(missingPkgs, packagePR.Repo)
|
||||
continue
|
||||
}
|
||||
var msg string
|
||||
switch packageBuildStatus {
|
||||
case BuildStatusSummarySuccess:
|
||||
msg = fmt.Sprintf("Build successful, for more information go in %s/project/show/%s.\n", ObsWebHost, stagingProject)
|
||||
case BuildStatusSummaryFailed:
|
||||
msg = fmt.Sprintf("Build failed, for more information go in %s/project/show/%s.\n", ObsWebHost, stagingProject)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
commentOnPackagePR(gitea, packagePR.Org, packagePR.Repo, packagePR.Num, msg)
|
||||
}
|
||||
|
||||
if len(missingPkgs) > 0 {
|
||||
overallBuildStatus = BuildStatusSummaryFailed
|
||||
msg := "The following packages were not found in the staging project:\n"
|
||||
for _, pkg := range missingPkgs {
|
||||
msg = msg + " - " + pkg + "\n"
|
||||
}
|
||||
common.LogInfo(msg)
|
||||
err := gitea.AddComment(pr, msg)
|
||||
if err != nil {
|
||||
common.LogError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
common.LogInfo("Build status:", overallBuildStatus)
|
||||
common.LogInfo("Build status:", buildStatus)
|
||||
if !IsDryRun {
|
||||
if err = SetStatus(gitea, org, repo, pr.Head.Sha, status); err != nil {
|
||||
return false, err
|
||||
@@ -1139,7 +1047,6 @@ func PollWorkNotifications(giteaUrl string) {
|
||||
|
||||
var ListPullNotificationsOnly bool
|
||||
var GiteaUrl string
|
||||
var ObsApiHost string
|
||||
var ObsWebHost string
|
||||
var IsDryRun bool
|
||||
var ProcessPROnly string
|
||||
@@ -1162,8 +1069,8 @@ func main() {
|
||||
flag.BoolVar(&ListPullNotificationsOnly, "list-notifications-only", false, "Only lists notifications without acting on them")
|
||||
ProcessPROnly := flag.String("pr", "", "Process only specific PR and ignore the rest. Use for debugging")
|
||||
buildRoot := flag.String("build-root", "", "Default build location for staging projects. Default is bot's home project")
|
||||
flag.StringVar(&GiteaUrl, "gitea-url", "", "Gitea instance")
|
||||
flag.StringVar(&ObsApiHost, "obs", "", "API for OBS instance")
|
||||
flag.StringVar(&GiteaUrl, "gitea-url", "https://src.opensuse.org", "Gitea instance")
|
||||
obsApiHost := flag.String("obs", "https://api.opensuse.org", "API for OBS instance")
|
||||
flag.StringVar(&ObsWebHost, "obs-web", "", "Web OBS instance, if not derived from the obs config")
|
||||
flag.BoolVar(&IsDryRun, "dry", false, "Dry-run, don't actually create any build projects or review changes")
|
||||
debug := flag.Bool("debug", false, "Turns on debug logging")
|
||||
@@ -1175,34 +1082,19 @@ func main() {
|
||||
common.SetLoggingLevel(common.LogLevelInfo)
|
||||
}
|
||||
|
||||
if len(GiteaUrl) == 0 {
|
||||
GiteaUrl = os.Getenv(common.GiteaHostEnv)
|
||||
}
|
||||
if len(GiteaUrl) == 0 {
|
||||
GiteaUrl = "https://src.opensuse.org"
|
||||
}
|
||||
if len(ObsApiHost) == 0 {
|
||||
ObsApiHost = os.Getenv(common.ObsApiEnv)
|
||||
}
|
||||
if len(ObsApiHost) == 0 {
|
||||
ObsApiHost = "https://api.opensuse.org"
|
||||
}
|
||||
if len(ObsWebHost) == 0 {
|
||||
ObsWebHost = os.Getenv(common.ObsWebEnv)
|
||||
}
|
||||
if len(ObsWebHost) == 0 {
|
||||
ObsWebHost = "https://build.opensuse.org"
|
||||
ObsWebHost = ObsWebHostFromApiHost(*obsApiHost)
|
||||
}
|
||||
|
||||
common.LogDebug("OBS Gitea Host:", GiteaUrl)
|
||||
common.LogDebug("OBS Web Host:", ObsWebHost)
|
||||
common.LogDebug("OBS API Host:", ObsApiHost)
|
||||
common.LogDebug("OBS API Host:", *obsApiHost)
|
||||
|
||||
common.PanicOnErrorWithMsg(common.RequireGiteaSecretToken(), "Cannot find GITEA_TOKEN")
|
||||
common.PanicOnErrorWithMsg(common.RequireObsSecretToken(), "Cannot find OBS_USER and OBS_PASSWORD")
|
||||
|
||||
var err error
|
||||
if ObsClient, err = common.NewObsClient(ObsApiHost); err != nil {
|
||||
if ObsClient, err = common.NewObsClient(*obsApiHost); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -105,10 +105,10 @@ func ProjectStatusSummarySvg(res []*common.BuildResult) []byte {
|
||||
|
||||
func LinkToBuildlog(R *common.BuildResult, S *common.PackageBuildStatus) string {
|
||||
if R != nil && S != nil {
|
||||
//switch S.Code {
|
||||
//case "succeeded", "failed", "building":
|
||||
switch S.Code {
|
||||
case "succeeded", "failed", "building":
|
||||
return "/buildlog/" + url.PathEscape(R.Project) + "/" + url.PathEscape(S.Package) + "/" + url.PathEscape(R.Repository) + "/" + url.PathEscape(R.Arch)
|
||||
//}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -170,7 +170,6 @@ func BuildStatusSvg(repo *common.BuildResult, status *common.PackageBuildStatus)
|
||||
buildStatus, ok := common.ObsBuildStatusDetails[status.Code]
|
||||
if !ok {
|
||||
buildStatus = common.ObsBuildStatusDetails["error"]
|
||||
common.LogError("Cannot find detail for status.Code", status.Code)
|
||||
}
|
||||
fillColor := "#480" // orange
|
||||
textColor := "#888"
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"src.opensuse.org/autogits/common"
|
||||
)
|
||||
|
||||
func WriteNewMaintainershipFile(m *common.MaintainershipMap, filename string) {
|
||||
f, err := os.Create(filename + ".new")
|
||||
common.PanicOnError(err)
|
||||
common.PanicOnError(m.WriteMaintainershipFile(f))
|
||||
common.PanicOnError(f.Sync())
|
||||
common.PanicOnError(f.Close())
|
||||
common.PanicOnError(os.Rename(filename+".new", filename))
|
||||
}
|
||||
|
||||
func run() error {
|
||||
pkg := flag.String("package", "", "Package to modify")
|
||||
rm := flag.Bool("rm", false, "Remove maintainer from package")
|
||||
add := flag.Bool("add", false, "Add maintainer to package")
|
||||
lint := flag.Bool("lint-only", false, "Reformat entire _maintainership.json only")
|
||||
flag.Parse()
|
||||
|
||||
if (*add == *rm) && !*lint {
|
||||
return fmt.Errorf("Need to either add or remove a maintainer, or lint")
|
||||
}
|
||||
|
||||
filename := common.MaintainershipFile
|
||||
if *lint {
|
||||
if len(flag.Args()) > 0 {
|
||||
filename = flag.Arg(0)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := common.ParseMaintainershipData(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse JSON: %w", err)
|
||||
}
|
||||
|
||||
if *lint {
|
||||
m.Raw = nil // forces a rewrite
|
||||
} else {
|
||||
users := flag.Args()
|
||||
if len(users) > 0 {
|
||||
maintainers, ok := m.Data[*pkg]
|
||||
if !ok && !*add {
|
||||
return fmt.Errorf("No package %s and not adding one.", *pkg)
|
||||
}
|
||||
|
||||
if *add {
|
||||
for _, u := range users {
|
||||
if !slices.Contains(maintainers, u) {
|
||||
maintainers = append(maintainers, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *rm {
|
||||
newMaintainers := make([]string, 0, len(maintainers))
|
||||
for _, m := range maintainers {
|
||||
if !slices.Contains(users, m) {
|
||||
newMaintainers = append(newMaintainers, m)
|
||||
}
|
||||
}
|
||||
maintainers = newMaintainers
|
||||
}
|
||||
|
||||
if len(maintainers) > 0 {
|
||||
slices.Sort(maintainers)
|
||||
m.Data[*pkg] = maintainers
|
||||
} else {
|
||||
delete(m.Data, *pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WriteNewMaintainershipFile(m, filename)
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
common.LogError(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"src.opensuse.org/autogits/common"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if os.Getenv("BE_MAIN") == "1" {
|
||||
main()
|
||||
return
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inData string
|
||||
expectedOut string
|
||||
params []string
|
||||
expectedError string
|
||||
isDir bool
|
||||
}{
|
||||
{
|
||||
name: "add user to existing package",
|
||||
inData: `{"pkg1": ["user1"]}`,
|
||||
params: []string{"-package", "pkg1", "-add", "user2"},
|
||||
expectedOut: `{"pkg1": ["user1", "user2"]}`,
|
||||
},
|
||||
{
|
||||
name: "add user to new package",
|
||||
inData: `{"pkg1": ["user1"]}`,
|
||||
params: []string{"-package", "pkg2", "-add", "user2"},
|
||||
expectedOut: `{"pkg1": ["user1"], "pkg2": ["user2"]}`,
|
||||
},
|
||||
{
|
||||
name: "no-op with no users",
|
||||
inData: `{"pkg1": ["user1"]}`,
|
||||
params: []string{"-package", "pkg1", "-add"},
|
||||
expectedOut: `{"pkg1": ["user1"]}`,
|
||||
},
|
||||
{
|
||||
name: "add existing user",
|
||||
inData: `{"pkg1": ["user1", "user2"]}`,
|
||||
params: []string{"-package", "pkg1", "-add", "user2"},
|
||||
expectedOut: `{"pkg1": ["user1", "user2"]}`,
|
||||
},
|
||||
{
|
||||
name: "remove user from package",
|
||||
inData: `{"pkg1": ["user1", "user2"]}`,
|
||||
params: []string{"-package", "pkg1", "-rm", "user2"},
|
||||
expectedOut: `{"pkg1": ["user1"]}`,
|
||||
},
|
||||
{
|
||||
name: "remove last user from package",
|
||||
inData: `{"pkg1": ["user1"]}`,
|
||||
params: []string{"-package", "pkg1", "-rm", "user1"},
|
||||
expectedOut: `{}`,
|
||||
},
|
||||
{
|
||||
name: "remove non-existent user",
|
||||
inData: `{"pkg1": ["user1"]}`,
|
||||
params: []string{"-package", "pkg1", "-rm", "user2"},
|
||||
expectedOut: `{"pkg1": ["user1"]}`,
|
||||
},
|
||||
{
|
||||
name: "lint only unsorted",
|
||||
inData: `{"pkg1": ["user2", "user1"]}`,
|
||||
params: []string{"-lint-only"},
|
||||
expectedOut: `{"pkg1": ["user1", "user2"]}`,
|
||||
},
|
||||
{
|
||||
name: "lint only no changes",
|
||||
inData: `{"pkg1": ["user1", "user2"]}`,
|
||||
params: []string{"-lint-only"},
|
||||
expectedOut: `{"pkg1": ["user1", "user2"]}`,
|
||||
},
|
||||
{
|
||||
name: "no file",
|
||||
params: []string{"-add"},
|
||||
expectedError: "no such file or directory",
|
||||
},
|
||||
{
|
||||
name: "invalid json",
|
||||
inData: `{"pkg1": ["user1"`,
|
||||
params: []string{"-add"},
|
||||
expectedError: "Failed to parse JSON",
|
||||
},
|
||||
{
|
||||
name: "add",
|
||||
inData: `{"pkg1": ["user1", "user2"]}`,
|
||||
params: []string{"-package", "pkg1", "-add", "user3"},
|
||||
expectedOut: `{"pkg1": ["user1", "user2", "user3"]}`,
|
||||
},
|
||||
{
|
||||
name: "lint specific file",
|
||||
inData: `{"pkg1": ["user2", "user1"]}`,
|
||||
params: []string{"-lint-only", "other.json"},
|
||||
expectedOut: `{"pkg1": ["user1", "user2"]}`,
|
||||
},
|
||||
{
|
||||
name: "add user to package when it was not there before",
|
||||
inData: `{}`,
|
||||
params: []string{"-package", "newpkg", "-add", "user1"},
|
||||
expectedOut: `{"newpkg": ["user1"]}`,
|
||||
},
|
||||
{
|
||||
name: "unreadable file (is a directory)",
|
||||
isDir: true,
|
||||
params: []string{"-rm"},
|
||||
expectedError: "is a directory",
|
||||
},
|
||||
{
|
||||
name: "remove user from non-existent package",
|
||||
inData: `{"pkg1": ["user1"]}`,
|
||||
params: []string{"-package", "pkg2", "-rm", "user2"},
|
||||
expectedError: "No package pkg2 and not adding one.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
oldWd, _ := os.Getwd()
|
||||
_ = os.Chdir(dir)
|
||||
defer os.Chdir(oldWd)
|
||||
|
||||
targetFile := common.MaintainershipFile
|
||||
if tt.name == "lint specific file" {
|
||||
targetFile = "other.json"
|
||||
}
|
||||
|
||||
if tt.isDir {
|
||||
_ = os.Mkdir(targetFile, 0755)
|
||||
} else if tt.inData != "" {
|
||||
_ = os.WriteFile(targetFile, []byte(tt.inData), 0644)
|
||||
}
|
||||
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||
|
||||
os.Args = append([]string{"cmd"}, tt.params...)
|
||||
err := run()
|
||||
|
||||
if tt.expectedError != "" {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error containing %q, but got none", tt.expectedError)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.expectedError) {
|
||||
t.Fatalf("expected error containing %q, got %q", tt.expectedError, err.Error())
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if tt.expectedOut != "" {
|
||||
data, _ := os.ReadFile(targetFile)
|
||||
var got, expected map[string][]string
|
||||
_ = json.Unmarshal(data, &got)
|
||||
_ = json.Unmarshal([]byte(tt.expectedOut), &expected)
|
||||
|
||||
if len(got) == 0 && len(expected) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMainRecursive(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inData string
|
||||
expectedOut string
|
||||
params []string
|
||||
expectExit bool
|
||||
}{
|
||||
{
|
||||
name: "test main() via recursive call",
|
||||
inData: `{"pkg1": ["user1"]}`,
|
||||
params: []string{"-package", "pkg1", "-add", "user2"},
|
||||
expectedOut: `{"pkg1": ["user1", "user2"]}`,
|
||||
},
|
||||
{
|
||||
name: "test main() failure",
|
||||
params: []string{"-package", "pkg1"},
|
||||
expectExit: true,
|
||||
},
|
||||
}
|
||||
|
||||
exe, _ := os.Executable()
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
oldWd, _ := os.Getwd()
|
||||
_ = os.Chdir(dir)
|
||||
defer os.Chdir(oldWd)
|
||||
|
||||
if tt.inData != "" {
|
||||
_ = os.WriteFile(common.MaintainershipFile, []byte(tt.inData), 0644)
|
||||
}
|
||||
|
||||
cmd := exec.Command(exe, append([]string{"-test.run=None"}, tt.params...)...)
|
||||
cmd.Env = append(os.Environ(), "BE_MAIN=1")
|
||||
out, runErr := cmd.CombinedOutput()
|
||||
|
||||
if tt.expectExit {
|
||||
if runErr == nil {
|
||||
t.Fatalf("expected exit with error, but it succeeded")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if runErr != nil {
|
||||
t.Fatalf("unexpected error: %v: %s", runErr, string(out))
|
||||
}
|
||||
|
||||
if tt.expectedOut != "" {
|
||||
data, _ := os.ReadFile(common.MaintainershipFile)
|
||||
var got, expected map[string][]string
|
||||
_ = json.Unmarshal(data, &got)
|
||||
_ = json.Unmarshal([]byte(tt.expectedOut), &expected)
|
||||
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
t.Fatalf("expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ Config file
|
||||
| *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* | If submitter is a maintainer, require review from another maintainer if available. | 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 | | `[]` |
|
||||
@@ -160,4 +160,4 @@ Server configuration
|
||||
|
||||
| Field | Type | Notes |
|
||||
| ----- | ----- | ----- |
|
||||
| root | Array of string | Format **org/repo\#branch** |
|
||||
| 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
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"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) {
|
||||
var buf bytes.Buffer
|
||||
origLogger := log.Writer()
|
||||
@@ -46,7 +125,7 @@ func TestUpdatePrBranch(t *testing.T) {
|
||||
req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
|
||||
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, "push", "origin", "+HEAD:+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:
|
||||
//
|
||||
// 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 main
|
||||
// Package mock_main is a generated GoMock package.
|
||||
package mock_main
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
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.
|
||||
@@ -42,9 +42,11 @@ func (m *MockStateChecker) EXPECT() *MockStateCheckerMockRecorder {
|
||||
}
|
||||
|
||||
// CheckRepos mocks base method.
|
||||
func (m *MockStateChecker) CheckRepos() {
|
||||
func (m *MockStateChecker) CheckRepos() error {
|
||||
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.
|
||||
@@ -60,19 +62,19 @@ type MockStateCheckerCheckReposCall struct {
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockStateCheckerCheckReposCall) Return() *MockStateCheckerCheckReposCall {
|
||||
c.Call = c.Call.Return()
|
||||
func (c *MockStateCheckerCheckReposCall) Return(arg0 error) *MockStateCheckerCheckReposCall {
|
||||
c.Call = c.Call.Return(arg0)
|
||||
return c
|
||||
}
|
||||
|
||||
// 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)
|
||||
return c
|
||||
}
|
||||
|
||||
// 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)
|
||||
return c
|
||||
}
|
||||
@@ -116,10 +118,10 @@ func (c *MockStateCheckerConsistencyCheckProcessCall) DoAndReturn(f func() error
|
||||
}
|
||||
|
||||
// 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()
|
||||
ret := m.ctrl.Call(m, "VerifyProjectState", configs)
|
||||
ret0, _ := ret[0].([]*PRToProcess)
|
||||
ret0, _ := ret[0].([]*interfaces.PRToProcess)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -137,81 +139,19 @@ type MockStateCheckerVerifyProjectStateCall struct {
|
||||
}
|
||||
|
||||
// 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)
|
||||
return c
|
||||
}
|
||||
|
||||
// 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)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*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 {
|
||||
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package main
|
||||
|
||||
//go:generate mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -24,7 +26,6 @@ func PrjGitDescription(prset *common.PRSet) (title string, desc string) {
|
||||
title_refs := make([]string, 0, len(prset.PRs)-1)
|
||||
refs := make([]string, 0, len(prset.PRs)-1)
|
||||
|
||||
prefix := ""
|
||||
for _, pr := range prset.PRs {
|
||||
if prset.IsPrjGitPR(pr.PR) {
|
||||
continue
|
||||
@@ -33,9 +34,6 @@ func PrjGitDescription(prset *common.PRSet) (title string, desc string) {
|
||||
// remove PRs that are not open from description
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(pr.PR.Title, "WIP:") {
|
||||
prefix = "WIP: "
|
||||
}
|
||||
org, repo, idx := pr.PRComponents()
|
||||
|
||||
title_refs = append(title_refs, repo)
|
||||
@@ -46,7 +44,7 @@ func PrjGitDescription(prset *common.PRSet) (title string, desc string) {
|
||||
slices.Sort(title_refs)
|
||||
slices.Sort(refs)
|
||||
|
||||
title = prefix + "Forwarded PRs: " + strings.Join(title_refs, ", ")
|
||||
title = "Forwarded PRs: " + strings.Join(title_refs, ", ")
|
||||
desc = fmt.Sprintf("This is a forwarded pull request by %s\nreferencing the following pull request(s):\n\n", GitAuthor) + strings.Join(refs, "\n") + "\n"
|
||||
|
||||
if prset.Config.ManualMergeOnly {
|
||||
@@ -291,9 +289,6 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
|
||||
|
||||
git := pr.git
|
||||
if len(prset.PRs) == 1 {
|
||||
if len(PrjGitPR.RemoteName) == 0 {
|
||||
PrjGitPR.RemoteName, _ = git.GitClone(common.DefaultGitPrj, "", PrjGitPR.PR.Base.Repo.SSHURL)
|
||||
}
|
||||
git.GitExecOrPanic(common.DefaultGitPrj, "fetch", PrjGitPR.RemoteName, PrjGitPR.PR.Head.Sha)
|
||||
common.LogDebug("Only project git in PR. Nothing to update.")
|
||||
return nil
|
||||
@@ -505,7 +500,7 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
|
||||
// make sure that prjgit is consistent and only submodules that are to be *updated*
|
||||
// reset anything that changed that is not part of the prset
|
||||
// package removals/additions are *not* counted here
|
||||
|
||||
org, repo, branch := config.GetPrjGit()
|
||||
// TODO: this is broken...
|
||||
if pr, err := prset.GetPrjGitPR(); err == nil && false {
|
||||
common.LogDebug("Submodule parse begin")
|
||||
@@ -554,7 +549,7 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
|
||||
} else {
|
||||
common.LogInfo("* No prjgit")
|
||||
}
|
||||
maintainers, err := common.FetchProjectMaintainershipData(Gitea, config)
|
||||
maintainers, err := common.FetchProjectMaintainershipData(Gitea, org, repo, branch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -605,7 +600,7 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
|
||||
common.LogError("merge error:", err)
|
||||
}
|
||||
} else {
|
||||
err = prset.AssignReviewers(Gitea, maintainers)
|
||||
prset.AssignReviewers(Gitea, maintainers)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -631,15 +626,6 @@ func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig)
|
||||
return PRProcessor.Process(pr)
|
||||
}
|
||||
|
||||
func (w *RequestProcessor) Process(pr *models.PullRequest) error {
|
||||
configs, ok := w.configuredRepos[pr.Base.Repo.Owner.UserName]
|
||||
if !ok {
|
||||
common.LogError("*** Cannot find config for org:", pr.Base.Repo.Owner.UserName)
|
||||
return fmt.Errorf("*** Cannot find config for org: %s", pr.Base.Repo.Owner.UserName)
|
||||
}
|
||||
return ProcesPullRequest(pr, configs)
|
||||
}
|
||||
|
||||
func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"go.uber.org/mock/gomock"
|
||||
"src.opensuse.org/autogits/common"
|
||||
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||
mock_common "src.opensuse.org/autogits/common/mock"
|
||||
)
|
||||
@@ -16,7 +17,7 @@ func TestOpenPR(t *testing.T) {
|
||||
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||
Branch: "branch",
|
||||
Organization: "test",
|
||||
GitProjectName: "prj#testing",
|
||||
GitProjectName: "prj",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -25,7 +26,6 @@ func TestOpenPR(t *testing.T) {
|
||||
Number: 1,
|
||||
Pull_Request: &common.PullRequest{
|
||||
Id: 1,
|
||||
Number: 1,
|
||||
Base: common.Head{
|
||||
Ref: "branch",
|
||||
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{
|
||||
GitCommiter: "tester",
|
||||
GitEmail: "test@suse.com",
|
||||
@@ -110,47 +60,14 @@ func TestOpenPR(t *testing.T) {
|
||||
|
||||
t.Run("PR git opened request against PrjGit == no action", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
Gitea = gitea
|
||||
Gitea = mock_common.NewMockGitea(ctl)
|
||||
|
||||
git.GitPath = t.TempDir()
|
||||
|
||||
pr.config.GitProjectName = "testRepo#testing"
|
||||
pr.config.GitProjectName = "testRepo"
|
||||
event.Repository.Name = "testRepo"
|
||||
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()
|
||||
|
||||
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 {
|
||||
if err := pr.Process(event); err != nil {
|
||||
t.Error("Error PrjGit opened request. Should be no error.", err)
|
||||
}
|
||||
})
|
||||
@@ -158,52 +75,43 @@ func TestOpenPR(t *testing.T) {
|
||||
t.Run("Open PrjGit PR", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
|
||||
Gitea = gitea
|
||||
|
||||
event.Repository.Name = "testRepo"
|
||||
pr.config.GitProjectName = "prjcopy#testing"
|
||||
pr.config.GitProjectName = "prjcopy"
|
||||
git.GitPath = t.TempDir()
|
||||
|
||||
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()
|
||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
||||
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()
|
||||
prjgit := &models.Repository{
|
||||
SSHURL: "./prj",
|
||||
DefaultBranch: "testing",
|
||||
}
|
||||
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().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
||||
gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||
gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||
|
||||
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)
|
||||
err := pr.Process(event)
|
||||
if err != nil {
|
||||
t.Error("error:", err)
|
||||
}
|
||||
@@ -212,61 +120,30 @@ func TestOpenPR(t *testing.T) {
|
||||
t.Run("Cannot create prjgit repository", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
|
||||
Gitea = gitea
|
||||
|
||||
event.Repository.Name = "testRepo"
|
||||
pr.config.GitProjectName = "prjcopy#testing"
|
||||
pr.config.GitProjectName = "prjcopy"
|
||||
git.GitPath = t.TempDir()
|
||||
|
||||
setupGitForTests(t, git)
|
||||
failedErr := errors.New("Returned error here")
|
||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr).AnyTimes()
|
||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
||||
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(nil, failedErr)
|
||||
|
||||
mockGit := mock_common.NewMockGit(ctl)
|
||||
pr.git = mockGit
|
||||
|
||||
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 {
|
||||
err := pr.Process(event)
|
||||
if err != failedErr {
|
||||
t.Error("error:", err)
|
||||
}
|
||||
})
|
||||
t.Run("Cannot create PR", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
|
||||
Gitea = gitea
|
||||
|
||||
event.Repository.Name = "testRepo"
|
||||
pr.config.GitProjectName = "prjcopy#testing"
|
||||
pr.config.GitProjectName = "prjcopy"
|
||||
git.GitPath = t.TempDir()
|
||||
|
||||
setupGitForTests(t, git)
|
||||
@@ -275,37 +152,10 @@ func TestOpenPR(t *testing.T) {
|
||||
DefaultBranch: "testing",
|
||||
}
|
||||
failedErr := errors.New("Returned error here")
|
||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjgit, nil).AnyTimes()
|
||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr, false)
|
||||
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
|
||||
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr, false)
|
||||
|
||||
mockGit := mock_common.NewMockGit(ctl)
|
||||
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)
|
||||
err := pr.Process(event)
|
||||
if err != failedErr {
|
||||
t.Error("error:", err)
|
||||
}
|
||||
@@ -313,54 +163,44 @@ func TestOpenPR(t *testing.T) {
|
||||
t.Run("Open PrjGit PR", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
|
||||
Gitea = gitea
|
||||
|
||||
event.Repository.Name = "testRepo"
|
||||
pr.config.GitProjectName = "prjcopy#testing"
|
||||
pr.config.GitProjectName = "prjcopy"
|
||||
git.GitPath = t.TempDir()
|
||||
|
||||
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")
|
||||
// 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().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
||||
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("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||
gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||
|
||||
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()
|
||||
|
||||
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 {
|
||||
err := pr.Process(event)
|
||||
if errors.Unwrap(err) != failedErr {
|
||||
t.Error("error:", err)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/mock/gomock"
|
||||
@@ -11,147 +16,217 @@ import (
|
||||
)
|
||||
|
||||
func TestSyncPR(t *testing.T) {
|
||||
config := &common.AutogitConfig{
|
||||
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||
Branch: "testing",
|
||||
Organization: "test-org",
|
||||
GitProjectName: "test-prj#testing",
|
||||
pr := PRProcessor{
|
||||
config: &common.AutogitConfig{
|
||||
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||
Branch: "testing",
|
||||
Organization: "test",
|
||||
GitProjectName: "prj",
|
||||
},
|
||||
}
|
||||
|
||||
git := &common.GitHandlerImpl{
|
||||
GitCommiter: "tester",
|
||||
GitEmail: "test@suse.com",
|
||||
GitPath: t.TempDir(),
|
||||
}
|
||||
|
||||
processor := &PRProcessor{
|
||||
config: config,
|
||||
git: git,
|
||||
event := &common.PullRequestWebhookEvent{
|
||||
Action: "syncronized",
|
||||
Number: 42,
|
||||
Pull_Request: &common.PullRequest{
|
||||
Number: 42,
|
||||
Base: common.Head{
|
||||
Ref: "branch",
|
||||
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
|
||||
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{
|
||||
Index: 42,
|
||||
Body: "PR: test-org/test-prj#24",
|
||||
Body: "PR: test/prj#24",
|
||||
Base: &models.PRBranchInfo{
|
||||
Ref: "main",
|
||||
Ref: "branch",
|
||||
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
|
||||
Repo: &models.Repository{
|
||||
Name: "test-repo",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
DefaultBranch: "main",
|
||||
Name: "testRepo",
|
||||
Owner: &models.User{
|
||||
UserName: "test",
|
||||
},
|
||||
DefaultBranch: "main1",
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo{
|
||||
Ref: "branch",
|
||||
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
|
||||
Repo: &models.Repository{
|
||||
Name: "test-repo",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
DefaultBranch: "main",
|
||||
Name: "testRepo",
|
||||
Owner: &models.User{
|
||||
UserName: "test",
|
||||
},
|
||||
DefaultBranch: "main1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
PrjGitPR := &models.PullRequest{
|
||||
Title: "some pull request",
|
||||
Body: "PR: test-org/test-repo#42",
|
||||
Body: "PR: test/testRepo#42",
|
||||
Index: 24,
|
||||
Base: &models.PRBranchInfo{
|
||||
Ref: "testing",
|
||||
Repo: &models.Repository{
|
||||
Name: "test-prj",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
SSHURL: "url",
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo{
|
||||
Name: "PR_test-repo#42",
|
||||
Name: "testing",
|
||||
Sha: "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95",
|
||||
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)
|
||||
defer ctl.Finish()
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
Gitea = gitea
|
||||
Gitea = mock_common.NewMockGitea(ctl)
|
||||
|
||||
// Common expectations for FetchPRSet and downstream checks
|
||||
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()
|
||||
git.GitPath = t.TempDir()
|
||||
|
||||
prjGitRepoPR := &models.PullRequest{
|
||||
Index: 100,
|
||||
Base: &models.PRBranchInfo{
|
||||
Ref: "testing",
|
||||
Repo: &models.Repository{
|
||||
Name: "test-prj",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo{
|
||||
Ref: "branch",
|
||||
},
|
||||
pr.config.GitProjectName = "testRepo"
|
||||
event.Repository.Name = "testRepo"
|
||||
|
||||
if err := pr.Process(event); err != nil {
|
||||
t.Error("Error PrjGit sync request. Should be no error.", err)
|
||||
}
|
||||
})
|
||||
|
||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjGitRepoPR, nil).AnyTimes()
|
||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
||||
t.Run("Missing submodule in prjgit", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
mock := mock_common.NewMockGitea(ctl)
|
||||
|
||||
if err := processor.Process(prjGitRepoPR); err != nil {
|
||||
t.Errorf("Expected nil error for PrjGit sync request, got %v", err)
|
||||
pr.gitea = mock
|
||||
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) {
|
||||
ctl := gomock.NewController(t)
|
||||
defer ctl.Finish()
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
Gitea = gitea
|
||||
mock := mock_common.NewMockGitea(ctl)
|
||||
|
||||
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()
|
||||
pr.gitea = mock
|
||||
git.GitPath = t.TempDir()
|
||||
|
||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("not found")).AnyTimes()
|
||||
pr.config.GitProjectName = "prjGit"
|
||||
event.Repository.Name = "tester"
|
||||
|
||||
err := processor.Process(modelPR)
|
||||
// It should fail because it can't find the project PR linked in body
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for missing project PR, got nil")
|
||||
setupGitForTests(t, git)
|
||||
|
||||
expectedErr := errors.New("Missing PR should throw error")
|
||||
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) {
|
||||
var b bytes.Buffer
|
||||
w := log.Writer()
|
||||
log.SetOutput(&b)
|
||||
defer log.SetOutput(w)
|
||||
|
||||
ctl := gomock.NewController(t)
|
||||
defer ctl.Finish()
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
Gitea = gitea
|
||||
mock := mock_common.NewMockGitea(ctl)
|
||||
|
||||
Gitea = mock
|
||||
git.GitPath = t.TempDir()
|
||||
|
||||
pr.config.GitProjectName = "prjGit"
|
||||
event.Repository.Name = "testRepo"
|
||||
|
||||
setupGitForTests(t, git)
|
||||
|
||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(PrjGitPR, 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()
|
||||
// mock.EXPECT().GetAssociatedPrjGitPR(event).Return(PrjGitPR, nil)
|
||||
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)
|
||||
|
||||
// For UpdatePrjGitPR
|
||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
||||
err := pr.Process(event)
|
||||
|
||||
err := processor.Process(modelPR)
|
||||
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,945 +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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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("Only project git in PR - needs clone", func(t *testing.T) {
|
||||
prset := &common.PRSet{
|
||||
Config: config,
|
||||
PRs: []*common.PRInfo{
|
||||
{
|
||||
RemoteName: "", // Triggers GitClone
|
||||
PR: &models.PullRequest{
|
||||
Base: &models.PRBranchInfo{
|
||||
Name: "main",
|
||||
Repo: &models.Repository{
|
||||
Name: "test-prj",
|
||||
Owner: &models.User{UserName: "test-org"},
|
||||
SSHURL: "ssh://git@example.com/test-prj.git",
|
||||
},
|
||||
},
|
||||
Head: &models.PRBranchInfo{
|
||||
Sha: "sha1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
mockGit.EXPECT().GitClone(common.DefaultGitPrj, "", "ssh://git@example.com/test-prj.git").Return("origin", nil)
|
||||
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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/gitea-generated/models"
|
||||
"src.opensuse.org/autogits/workflow-pr/interfaces"
|
||||
)
|
||||
|
||||
type DefaultStateChecker struct {
|
||||
@@ -17,11 +18,11 @@ type DefaultStateChecker struct {
|
||||
checkOnStart bool
|
||||
checkInterval time.Duration
|
||||
|
||||
processor PullRequestProcessor
|
||||
i StateChecker
|
||||
processor *RequestProcessor
|
||||
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{
|
||||
checkInterval: interval,
|
||||
checkOnStart: checkOnStart,
|
||||
@@ -53,7 +54,7 @@ func (s *DefaultStateChecker) ProcessPR(pr *models.PullRequest, config *common.A
|
||||
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:
|
||||
for sub, commitID := range submodules {
|
||||
common.LogDebug(" + checking", sub, commitID)
|
||||
@@ -73,7 +74,7 @@ nextSubmodule:
|
||||
|
||||
branch = repo.DefaultBranch
|
||||
}
|
||||
prsToProcess = append(prsToProcess, &PRToProcess{
|
||||
prsToProcess = append(prsToProcess, &interfaces.PRToProcess{
|
||||
Org: config.Organization,
|
||||
Repo: submoduleName,
|
||||
Branch: branch,
|
||||
@@ -116,7 +117,7 @@ nextSubmodule:
|
||||
return prsToProcess, nil
|
||||
}
|
||||
|
||||
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) ([]*PRToProcess, error) {
|
||||
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) ([]*interfaces.PRToProcess, error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
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()
|
||||
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
|
||||
@@ -147,7 +148,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
|
||||
_, err = git.GitClone(prjGitRepo, prjGitBranch, repo.SSHURL)
|
||||
common.PanicOnError(err)
|
||||
|
||||
prsToProcess = append(prsToProcess, &PRToProcess{
|
||||
prsToProcess = append(prsToProcess, &interfaces.PRToProcess{
|
||||
Org: prjGitOrg,
|
||||
Repo: prjGitRepo,
|
||||
Branch: prjGitBranch,
|
||||
@@ -155,8 +156,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
|
||||
submodules, err := git.GitSubmoduleList(prjGitRepo, "HEAD")
|
||||
|
||||
// forward any package-gits referred by the project git, but don't go back
|
||||
subPrs, err := PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules)
|
||||
return append(prsToProcess, subPrs...), err
|
||||
return PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules)
|
||||
}
|
||||
|
||||
func (s *DefaultStateChecker) CheckRepos() {
|
||||
@@ -170,8 +170,7 @@ func (s *DefaultStateChecker) CheckRepos() {
|
||||
}
|
||||
}()
|
||||
|
||||
processor := s.processor.(*RequestProcessor)
|
||||
for org, configs := range processor.configuredRepos {
|
||||
for org, configs := range s.processor.configuredRepos {
|
||||
for _, config := range configs {
|
||||
if s.checkInterval > 0 {
|
||||
sleepInterval := (s.checkInterval - s.checkInterval/2) + time.Duration(rand.Int63n(int64(s.checkInterval)))
|
||||
|
||||
@@ -1,338 +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)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
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/gitea-generated/models"
|
||||
mock_common "src.opensuse.org/autogits/common/mock"
|
||||
mock_main "src.opensuse.org/autogits/workflow-pr/mock"
|
||||
)
|
||||
|
||||
func TestRepoCheck(t *testing.T) {
|
||||
@@ -21,15 +22,16 @@ func TestRepoCheck(t *testing.T) {
|
||||
t.Run("Consistency Check On Start", func(t *testing.T) {
|
||||
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
||||
ctl := gomock.NewController(t)
|
||||
state := NewMockStateChecker(ctl)
|
||||
state := mock_main.NewMockStateChecker(ctl)
|
||||
c.i = state
|
||||
state.EXPECT().CheckRepos().Do(func() {
|
||||
state.EXPECT().CheckRepos().Do(func() error {
|
||||
// only checkOnStart has checkInterval = 0
|
||||
if c.checkInterval != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
c.exitCheckLoop = true
|
||||
return nil
|
||||
})
|
||||
|
||||
c.ConsistencyCheckProcess()
|
||||
@@ -41,11 +43,11 @@ func TestRepoCheck(t *testing.T) {
|
||||
t.Run("No consistency Check On Start", func(t *testing.T) {
|
||||
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
||||
ctl := gomock.NewController(t)
|
||||
state := NewMockStateChecker(ctl)
|
||||
state := mock_main.NewMockStateChecker(ctl)
|
||||
c.i = state
|
||||
|
||||
nCalls := 10
|
||||
state.EXPECT().CheckRepos().Do(func() {
|
||||
state.EXPECT().CheckRepos().Do(func() error {
|
||||
// only checkOnStart has checkInterval = 0
|
||||
if c.checkInterval != 100 {
|
||||
t.Fail()
|
||||
@@ -55,6 +57,7 @@ func TestRepoCheck(t *testing.T) {
|
||||
if nCalls == 0 {
|
||||
c.exitCheckLoop = true
|
||||
}
|
||||
return nil
|
||||
}).Times(nCalls)
|
||||
c.checkOnStart = false
|
||||
|
||||
@@ -63,9 +66,8 @@ func TestRepoCheck(t *testing.T) {
|
||||
|
||||
t.Run("CheckRepos() calls CheckProjectState() for each project", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
state := NewMockStateChecker(ctl)
|
||||
state := mock_main.NewMockStateChecker(ctl)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
|
||||
config1 := &common.AutogitConfig{
|
||||
GitProjectName: "git_repo1",
|
||||
@@ -95,14 +97,15 @@ func TestRepoCheck(t *testing.T) {
|
||||
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo2_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) {
|
||||
ctl := gomock.NewController(t)
|
||||
state := NewMockStateChecker(ctl)
|
||||
state := mock_main.NewMockStateChecker(ctl)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
|
||||
config1 := &common.AutogitConfig{
|
||||
GitProjectName: "git_repo1",
|
||||
@@ -122,7 +125,11 @@ func TestRepoCheck(t *testing.T) {
|
||||
err := errors.New("test error")
|
||||
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo1_org"][0]).Return(nil, err)
|
||||
|
||||
c.CheckRepos()
|
||||
r := c.CheckRepos()
|
||||
|
||||
if !errors.Is(r, err) {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -147,7 +154,6 @@ func TestVerifyProjectState(t *testing.T) {
|
||||
t.Run("Project state with no PRs", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
|
||||
git := &common.GitHandlerImpl{
|
||||
GitCommiter: "TestCommiter",
|
||||
@@ -171,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",
|
||||
}, nil).AnyTimes()
|
||||
gitea.EXPECT().GetRecentPullRequests(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullRequest{}, nil).AnyTimes()
|
||||
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{}, nil).AnyTimes()
|
||||
}, nil)
|
||||
gitea.EXPECT().GetRecentPullRequests(org, "testRepo", "testing")
|
||||
gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())
|
||||
|
||||
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
||||
/*
|
||||
@@ -193,7 +199,7 @@ func TestVerifyProjectState(t *testing.T) {
|
||||
t.Run("Project state with 1 PRs that doesn't trigger updates", func(t *testing.T) {
|
||||
ctl := gomock.NewController(t)
|
||||
gitea := mock_common.NewMockGitea(ctl)
|
||||
gitea.EXPECT().ResetTimelineCache(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
process := mock_main.NewMockPullRequestProcessor(ctl)
|
||||
|
||||
git := &common.GitHandlerImpl{
|
||||
GitCommiter: "TestCommiter",
|
||||
@@ -217,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",
|
||||
}, 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{
|
||||
ID: 1234,
|
||||
URL: "url here",
|
||||
@@ -253,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.git = &testGit{
|
||||
git: git,
|
||||
}*/
|
||||
// process.EXPECT().Process(gomock.Any())
|
||||
process.EXPECT().Process(gomock.Any(), gomock.Any(), gomock.Any())
|
||||
// c.processor.Opened = process
|
||||
|
||||
_, 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,87 +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{
|
||||
"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