Compare commits
3 Commits
maintainer
...
submodulem
| Author | SHA256 | Date | |
|---|---|---|---|
| 66a2c0565e | |||
| d5d6910906 | |||
| 444959540a |
@@ -23,6 +23,7 @@ jobs:
|
|||||||
- run: git checkout FETCH_HEAD
|
- run: git checkout FETCH_HEAD
|
||||||
- run: go generate -C common
|
- run: go generate -C common
|
||||||
- run: go generate -C workflow-pr
|
- run: go generate -C workflow-pr
|
||||||
|
- run: go generate -C workflow-pr/interfaces
|
||||||
- run: git add -N .; git diff
|
- run: git add -N .; git diff
|
||||||
- run: |
|
- run: |
|
||||||
status=$(git status --short)
|
status=$(git status --short)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ jobs:
|
|||||||
- run: git checkout FETCH_HEAD
|
- run: git checkout FETCH_HEAD
|
||||||
- run: go generate -C common
|
- run: go generate -C common
|
||||||
- run: go generate -C workflow-pr
|
- run: go generate -C workflow-pr
|
||||||
|
- run: go generate -C workflow-pr/interfaces
|
||||||
- run: |
|
- run: |
|
||||||
host=${{ gitea.server_url }}
|
host=${{ gitea.server_url }}
|
||||||
host=${host#https://}
|
host=${host#https://}
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
|||||||
*.osc
|
*.osc
|
||||||
*.conf
|
*.conf
|
||||||
|
utils/gitmodules-automerge/gitmodules-automerge
|
||||||
|
utils/hujson/hujson
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
|
|
||||||
Name: autogits
|
Name: autogits
|
||||||
Version: 1
|
Version: 0
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: GitWorkflow utilities
|
Summary: GitWorkflow utilities
|
||||||
License: GPL-2.0-or-later
|
License: GPL-2.0-or-later
|
||||||
@@ -41,7 +41,6 @@ Command-line tool to import devel projects from obs to git
|
|||||||
|
|
||||||
%package doc
|
%package doc
|
||||||
Summary: Common documentation files
|
Summary: Common documentation files
|
||||||
BuildArch: noarch
|
|
||||||
|
|
||||||
%description -n autogits-doc
|
%description -n autogits-doc
|
||||||
Common documentation files
|
Common documentation files
|
||||||
@@ -57,11 +56,10 @@ with a topic
|
|||||||
|
|
||||||
|
|
||||||
%package gitea-status-proxy
|
%package gitea-status-proxy
|
||||||
Summary: Proxy for setting commit status in Gitea
|
Summary: gitea-status-proxy
|
||||||
|
|
||||||
%description gitea-status-proxy
|
%description gitea-status-proxy
|
||||||
Setting commit status requires code write access token. This proxy
|
|
||||||
is middleware that delegates status setting without access to other APIs
|
|
||||||
|
|
||||||
%package group-review
|
%package group-review
|
||||||
Summary: Reviews of groups defined in ProjectGit
|
Summary: Reviews of groups defined in ProjectGit
|
||||||
@@ -98,6 +96,7 @@ Provides: /usr/bin/hujson
|
|||||||
|
|
||||||
%description utils
|
%description utils
|
||||||
HuJSON to JSON parser, using stdin -> stdout pipe
|
HuJSON to JSON parser, using stdin -> stdout pipe
|
||||||
|
gitmodules-automerge fixes conflicts in .gitmodules conflicts during merge
|
||||||
|
|
||||||
|
|
||||||
%package workflow-direct
|
%package workflow-direct
|
||||||
@@ -130,7 +129,7 @@ go build \
|
|||||||
-C utils/hujson \
|
-C utils/hujson \
|
||||||
-buildmode=pie
|
-buildmode=pie
|
||||||
go build \
|
go build \
|
||||||
-C utils/maintainer-update \
|
-C utils/gitmodules-automerge \
|
||||||
-buildmode=pie
|
-buildmode=pie
|
||||||
go build \
|
go build \
|
||||||
-C gitea-events-rabbitmq-publisher \
|
-C gitea-events-rabbitmq-publisher \
|
||||||
@@ -172,17 +171,15 @@ install -D -m0755 gitea-events-rabbitmq-publisher/gitea-events-rabbitmq-publishe
|
|||||||
install -D -m0644 systemd/gitea-events-rabbitmq-publisher.service %{buildroot}%{_unitdir}/gitea-events-rabbitmq-publisher.service
|
install -D -m0644 systemd/gitea-events-rabbitmq-publisher.service %{buildroot}%{_unitdir}/gitea-events-rabbitmq-publisher.service
|
||||||
install -D -m0755 gitea_status_proxy/gitea_status_proxy %{buildroot}%{_bindir}/gitea_status_proxy
|
install -D -m0755 gitea_status_proxy/gitea_status_proxy %{buildroot}%{_bindir}/gitea_status_proxy
|
||||||
install -D -m0755 group-review/group-review %{buildroot}%{_bindir}/group-review
|
install -D -m0755 group-review/group-review %{buildroot}%{_bindir}/group-review
|
||||||
install -D -m0644 systemd/group-review@.service %{buildroot}%{_unitdir}/group-review@.service
|
|
||||||
install -D -m0755 obs-forward-bot/obs-forward-bot %{buildroot}%{_bindir}/obs-forward-bot
|
install -D -m0755 obs-forward-bot/obs-forward-bot %{buildroot}%{_bindir}/obs-forward-bot
|
||||||
install -D -m0755 obs-staging-bot/obs-staging-bot %{buildroot}%{_bindir}/obs-staging-bot
|
install -D -m0755 obs-staging-bot/obs-staging-bot %{buildroot}%{_bindir}/obs-staging-bot
|
||||||
install -D -m0644 systemd/obs-staging-bot.service %{buildroot}%{_unitdir}/obs-staging-bot.service
|
install -D -m0644 systemd/obs-staging-bot.service %{buildroot}%{_unitdir}/obs-staging-bot.service
|
||||||
install -D -m0755 obs-status-service/obs-status-service %{buildroot}%{_bindir}/obs-status-service
|
install -D -m0755 obs-status-service/obs-status-service %{buildroot}%{_bindir}/obs-status-service
|
||||||
install -D -m0644 systemd/obs-status-service.service %{buildroot}%{_unitdir}/obs-status-service.service
|
install -D -m0644 systemd/obs-status-service.service %{buildroot}%{_unitdir}/obs-status-service.service
|
||||||
install -D -m0755 workflow-direct/workflow-direct %{buildroot}%{_bindir}/workflow-direct
|
install -D -m0755 workflow-direct/workflow-direct %{buildroot}%{_bindir}/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 workflow-pr/workflow-pr %{buildroot}%{_bindir}/workflow-pr
|
||||||
install -D -m0755 utils/hujson/hujson %{buildroot}%{_bindir}/hujson
|
install -D -m0755 utils/hujson/hujson %{buildroot}%{_bindir}/hujson
|
||||||
install -D -m0755 utils/maintainer-update/maintainer-update %{buildroot}${_bindir}/maintainer-update
|
install -D -m0755 utils/gitmodules-automerge/gitmodules-automerge %{buildroot}%{_bindir}/gitmodules-automerge
|
||||||
|
|
||||||
%pre gitea-events-rabbitmq-publisher
|
%pre gitea-events-rabbitmq-publisher
|
||||||
%service_add_pre gitea-events-rabbitmq-publisher.service
|
%service_add_pre gitea-events-rabbitmq-publisher.service
|
||||||
@@ -196,18 +193,6 @@ install -D -m0755 utils/maintainer-update/maintainer-update
|
|||||||
%postun gitea-events-rabbitmq-publisher
|
%postun gitea-events-rabbitmq-publisher
|
||||||
%service_del_postun gitea-events-rabbitmq-publisher.service
|
%service_del_postun gitea-events-rabbitmq-publisher.service
|
||||||
|
|
||||||
%pre group-review
|
|
||||||
%service_add_pre group-review@.service
|
|
||||||
|
|
||||||
%post group-review
|
|
||||||
%service_add_post group-review@.service
|
|
||||||
|
|
||||||
%preun group-review
|
|
||||||
%service_del_preun group-review@.service
|
|
||||||
|
|
||||||
%postun group-review
|
|
||||||
%service_del_postun group-review@.service
|
|
||||||
|
|
||||||
%pre obs-staging-bot
|
%pre obs-staging-bot
|
||||||
%service_add_pre obs-staging-bot.service
|
%service_add_pre obs-staging-bot.service
|
||||||
|
|
||||||
@@ -232,18 +217,6 @@ install -D -m0755 utils/maintainer-update/maintainer-update
|
|||||||
%postun obs-status-service
|
%postun obs-status-service
|
||||||
%service_del_postun obs-status-service.service
|
%service_del_postun obs-status-service.service
|
||||||
|
|
||||||
%pre workflow-pr
|
|
||||||
%service_add_pre workflow-direct@.service
|
|
||||||
|
|
||||||
%post workflow-pr
|
|
||||||
%service_add_post workflow-direct@.service
|
|
||||||
|
|
||||||
%preun workflow-pr
|
|
||||||
%service_del_preun workflow-direct@.service
|
|
||||||
|
|
||||||
%postun workflow-pr
|
|
||||||
%service_del_postun workflow-direct@.service
|
|
||||||
|
|
||||||
%files devel-importer
|
%files devel-importer
|
||||||
%license COPYING
|
%license COPYING
|
||||||
%doc devel-importer/README.md
|
%doc devel-importer/README.md
|
||||||
@@ -268,7 +241,6 @@ install -D -m0755 utils/maintainer-update/maintainer-update
|
|||||||
%license COPYING
|
%license COPYING
|
||||||
%doc group-review/README.md
|
%doc group-review/README.md
|
||||||
%{_bindir}/group-review
|
%{_bindir}/group-review
|
||||||
%{_unitdir}/group-review@.service
|
|
||||||
|
|
||||||
%files obs-forward-bot
|
%files obs-forward-bot
|
||||||
%license COPYING
|
%license COPYING
|
||||||
@@ -289,13 +261,12 @@ install -D -m0755 utils/maintainer-update/maintainer-update
|
|||||||
%files utils
|
%files utils
|
||||||
%license COPYING
|
%license COPYING
|
||||||
%{_bindir}/hujson
|
%{_bindir}/hujson
|
||||||
%{_bindir}/maintainer-update
|
%{_bindir}/gitmodules-automerge
|
||||||
|
|
||||||
%files workflow-direct
|
%files workflow-direct
|
||||||
%license COPYING
|
%license COPYING
|
||||||
%doc workflow-direct/README.md
|
%doc workflow-direct/README.md
|
||||||
%{_bindir}/workflow-direct
|
%{_bindir}/workflow-direct
|
||||||
%{_unitdir}/workflow-direct@.service
|
|
||||||
|
|
||||||
%files workflow-pr
|
%files workflow-pr
|
||||||
%license COPYING
|
%license COPYING
|
||||||
|
|||||||
@@ -61,20 +61,6 @@ type Permissions struct {
|
|||||||
Members []string
|
Members []string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
Label_StagingAuto = "staging/Auto"
|
|
||||||
Label_ReviewPending = "review/Pending"
|
|
||||||
Label_ReviewDone = "review/Done"
|
|
||||||
)
|
|
||||||
|
|
||||||
func LabelKey(tag_value string) string {
|
|
||||||
// capitalize first letter and remove /
|
|
||||||
if len(tag_value) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strings.ToUpper(tag_value[0:1]) + strings.ReplaceAll(tag_value[1:], "/", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
type AutogitConfig struct {
|
type AutogitConfig struct {
|
||||||
Workflows []string // [pr, direct, test]
|
Workflows []string // [pr, direct, test]
|
||||||
Organization string
|
Organization string
|
||||||
@@ -86,8 +72,6 @@ type AutogitConfig struct {
|
|||||||
Committers []string // group in addition to Reviewers and Maintainers that can order the bot around, mostly as helper for factory-maintainers
|
Committers []string // group in addition to Reviewers and Maintainers that can order the bot around, mostly as helper for factory-maintainers
|
||||||
Subdirs []string // list of directories to sort submodules into. Needed b/c _manifest cannot list non-existent directories
|
Subdirs []string // list of directories to sort submodules into. Needed b/c _manifest cannot list non-existent directories
|
||||||
|
|
||||||
Labels map[string]string // list of tags, if not default, to apply
|
|
||||||
|
|
||||||
NoProjectGitPR bool // do not automatically create project git PRs, just assign reviewers and assume somethign else creates the ProjectGit PR
|
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
|
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
|
ManualMergeProject bool // require merge of ProjectGit PRs with "Merge OK" by ProjectMaintainers and/or reviewers
|
||||||
@@ -204,8 +188,6 @@ func (configs AutogitConfigs) GetPrjGitConfig(org, repo, branch string) *Autogit
|
|||||||
if c.GitProjectName == prjgit {
|
if c.GitProjectName == prjgit {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for _, c := range configs {
|
|
||||||
if c.Organization == org && c.Branch == branch {
|
if c.Organization == org && c.Branch == branch {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -291,14 +273,6 @@ func (config *AutogitConfig) GetRemoteBranch() string {
|
|||||||
return "origin_" + config.Branch
|
return "origin_" + config.Branch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *AutogitConfig) Label(label string) string {
|
|
||||||
if t, found := config.Labels[LabelKey(label)]; found {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
return label
|
|
||||||
}
|
|
||||||
|
|
||||||
type StagingConfig struct {
|
type StagingConfig struct {
|
||||||
ObsProject string
|
ObsProject string
|
||||||
RebuildAll bool
|
RebuildAll bool
|
||||||
|
|||||||
@@ -10,67 +10,6 @@ import (
|
|||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
mock_common "src.opensuse.org/autogits/common/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLabelKey(t *testing.T) {
|
|
||||||
tests := map[string]string{
|
|
||||||
"": "",
|
|
||||||
"foo": "Foo",
|
|
||||||
"foo/bar": "Foobar",
|
|
||||||
"foo/Bar": "FooBar",
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range tests {
|
|
||||||
if c := common.LabelKey(k); c != v {
|
|
||||||
t.Error("expected", v, "got", c, "input", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfigLabelParser(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
json string
|
|
||||||
label_value string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
json: "{}",
|
|
||||||
label_value: "path/String",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "defined",
|
|
||||||
json: `{"Labels": {"foo": "bar", "PathString": "moo/Label"}}`,
|
|
||||||
label_value: "moo/Label",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "undefined",
|
|
||||||
json: `{"Labels": {"foo": "bar", "NotPathString": "moo/Label"}}`,
|
|
||||||
label_value: "path/String",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
repo := models.Repository{
|
|
||||||
DefaultBranch: "master",
|
|
||||||
}
|
|
||||||
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
gitea := mock_common.NewMockGiteaFileContentAndRepoFetcher(ctl)
|
|
||||||
gitea.EXPECT().GetRepositoryFileContent("foo", "bar", "", "workflow.config").Return([]byte(test.json), "abc", nil)
|
|
||||||
gitea.EXPECT().GetRepository("foo", "bar").Return(&repo, nil)
|
|
||||||
|
|
||||||
config, err := common.ReadWorkflowConfig(gitea, "foo/bar")
|
|
||||||
if err != nil || config == nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if l := config.Label("path/String"); l != test.label_value {
|
|
||||||
t.Error("Expecting", test.label_value, "got", l)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProjectConfigMatcher(t *testing.T) {
|
func TestProjectConfigMatcher(t *testing.T) {
|
||||||
configs := common.AutogitConfigs{
|
configs := common.AutogitConfigs{
|
||||||
{
|
{
|
||||||
@@ -82,15 +21,6 @@ func TestProjectConfigMatcher(t *testing.T) {
|
|||||||
Branch: "main",
|
Branch: "main",
|
||||||
GitProjectName: "test/prjgit#main",
|
GitProjectName: "test/prjgit#main",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Organization: "test",
|
|
||||||
Branch: "main",
|
|
||||||
GitProjectName: "test/bar#never_match",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Organization: "test",
|
|
||||||
GitProjectName: "test/bar#main",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -120,20 +50,6 @@ func TestProjectConfigMatcher(t *testing.T) {
|
|||||||
branch: "main",
|
branch: "main",
|
||||||
config: 1,
|
config: 1,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "prjgit only match",
|
|
||||||
org: "test",
|
|
||||||
repo: "bar",
|
|
||||||
branch: "main",
|
|
||||||
config: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "non-default branch match",
|
|
||||||
org: "test",
|
|
||||||
repo: "bar",
|
|
||||||
branch: "something_main",
|
|
||||||
config: -1,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@@ -189,10 +105,6 @@ func TestConfigWorkflowParser(t *testing.T) {
|
|||||||
if config.ManualMergeOnly != false {
|
if config.ManualMergeOnly != false {
|
||||||
t.Fatal("This should be false")
|
t.Fatal("This should be false")
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Label("foobar") != "foobar" {
|
|
||||||
t.Fatal("undefined label should return default value")
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
296
common/git_parser.go
Normal file
296
common/git_parser.go
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GitStatus_Untracked = 0
|
||||||
|
GitStatus_Modified = 1
|
||||||
|
GitStatus_Ignored = 2
|
||||||
|
GitStatus_Unmerged = 3 // States[0..3] -- Stage1, Stage2, Stage3 of merge objects
|
||||||
|
GitStatus_Renamed = 4 // orig name in States[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
type GitStatusData struct {
|
||||||
|
Path string
|
||||||
|
Status int
|
||||||
|
States [3]string
|
||||||
|
|
||||||
|
/*
|
||||||
|
<sub> A 4 character field describing the submodule state.
|
||||||
|
"N..." when the entry is not a submodule.
|
||||||
|
"S<c><m><u>" when the entry is a submodule.
|
||||||
|
<c> is "C" if the commit changed; otherwise ".".
|
||||||
|
<m> is "M" if it has tracked changes; otherwise ".".
|
||||||
|
<u> is "U" if there are untracked changes; otherwise ".".
|
||||||
|
*/
|
||||||
|
SubmoduleChanges string
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGit_HexString(data io.ByteReader) (string, error) {
|
||||||
|
str := make([]byte, 0, 32)
|
||||||
|
for {
|
||||||
|
c, err := data.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case c == 0 || c == ' ':
|
||||||
|
return string(str), nil
|
||||||
|
case c >= 'a' && c <= 'f':
|
||||||
|
case c >= 'A' && c <= 'F':
|
||||||
|
case c >= '0' && c <= '9':
|
||||||
|
default:
|
||||||
|
return "", errors.New("Invalid character in hex string:" + string(c))
|
||||||
|
}
|
||||||
|
str = append(str, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func parseGit_String(data io.ByteReader) (string, error) {
|
||||||
|
str := make([]byte, 0, 100)
|
||||||
|
for {
|
||||||
|
c, err := data.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("Unexpected EOF. Expected NUL string term")
|
||||||
|
}
|
||||||
|
if c == 0 || c == ' ' {
|
||||||
|
return string(str), nil
|
||||||
|
}
|
||||||
|
str = append(str, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGit_StringWithSpace(data io.ByteReader) (string, error) {
|
||||||
|
str := make([]byte, 0, 100)
|
||||||
|
for {
|
||||||
|
c, err := data.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("Unexpected EOF. Expected NUL string term")
|
||||||
|
}
|
||||||
|
if c == 0 {
|
||||||
|
return string(str), nil
|
||||||
|
}
|
||||||
|
str = append(str, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipGitStatusEntry(data io.ByteReader, skipSpaceLen int) error {
|
||||||
|
for skipSpaceLen > 0 {
|
||||||
|
c, err := data.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c == ' ' {
|
||||||
|
skipSpaceLen--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSingleStatusEntry(data io.ByteReader) (*GitStatusData, error) {
|
||||||
|
ret := GitStatusData{}
|
||||||
|
statusType, err := data.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
switch statusType {
|
||||||
|
case '1':
|
||||||
|
var err error
|
||||||
|
if err = skipGitStatusEntry(data, 8); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Status = GitStatus_Modified
|
||||||
|
ret.Path, err = parseGit_StringWithSpace(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case '2':
|
||||||
|
var err error
|
||||||
|
if err = skipGitStatusEntry(data, 9); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Status = GitStatus_Renamed
|
||||||
|
ret.Path, err = parseGit_StringWithSpace(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.States[0], err = parseGit_StringWithSpace(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case '?':
|
||||||
|
var err error
|
||||||
|
if err = skipGitStatusEntry(data, 1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Status = GitStatus_Untracked
|
||||||
|
ret.Path, err = parseGit_StringWithSpace(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case '!':
|
||||||
|
var err error
|
||||||
|
if err = skipGitStatusEntry(data, 1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Status = GitStatus_Ignored
|
||||||
|
ret.Path, err = parseGit_StringWithSpace(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case 'u':
|
||||||
|
var err error
|
||||||
|
if err = skipGitStatusEntry(data, 2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.SubmoduleChanges, err = parseGit_String(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = skipGitStatusEntry(data, 4); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.States[0], err = parseGit_HexString(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.States[1], err = parseGit_HexString(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.States[2], err = parseGit_HexString(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Status = GitStatus_Unmerged
|
||||||
|
ret.Path, err = parseGit_StringWithSpace(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Invalid status type" + string(statusType))
|
||||||
|
}
|
||||||
|
return &ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGitStatusData(data io.ByteReader) (Data, error) {
|
||||||
|
ret := make([]GitStatusData, 0, 10)
|
||||||
|
for {
|
||||||
|
data, err := parseSingleStatusEntry(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if data == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, *data)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Data interface{}
|
||||||
|
|
||||||
|
type CommitStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Add CommitStatus = iota
|
||||||
|
Rm
|
||||||
|
Copy
|
||||||
|
Modify
|
||||||
|
Rename
|
||||||
|
TypeChange
|
||||||
|
Unmerged
|
||||||
|
Unknown
|
||||||
|
)
|
||||||
|
|
||||||
|
type GitDiffRawData struct {
|
||||||
|
SrcMode, DstMode string
|
||||||
|
SrcCommit, DstCommit string
|
||||||
|
Status CommitStatus
|
||||||
|
Src, Dst string
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGit_DiffIndexStatus(data io.ByteReader, d *GitDiffRawData) error {
|
||||||
|
b, err := data.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch b {
|
||||||
|
case 'A':
|
||||||
|
d.Status = Add
|
||||||
|
case 'C':
|
||||||
|
d.Status = Copy
|
||||||
|
case 'D':
|
||||||
|
d.Status = Rm
|
||||||
|
case 'M':
|
||||||
|
d.Status = Modify
|
||||||
|
case 'R':
|
||||||
|
d.Status = Rename
|
||||||
|
case 'T':
|
||||||
|
d.Status = TypeChange
|
||||||
|
case 'U':
|
||||||
|
d.Status = Unmerged
|
||||||
|
case 'X':
|
||||||
|
return fmt.Errorf("Unexpected unknown change type. This is a git bug")
|
||||||
|
}
|
||||||
|
_, err = parseGit_StringWithSpace(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSingleGitDiffIndexRawData(data io.ByteReader) (*GitDiffRawData, error) {
|
||||||
|
var ret GitDiffRawData
|
||||||
|
|
||||||
|
b, err := data.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if b != ':' {
|
||||||
|
return nil, fmt.Errorf("Expected ':' but got '%s'", string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.SrcMode, err = parseGit_String(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.DstMode, err = parseGit_String(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.Src, err = parseGit_String(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret.Dst, err = parseGit_String(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = parseGit_DiffIndexStatus(data, &ret); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Dst = ret.Src
|
||||||
|
switch ret.Status {
|
||||||
|
case Copy, Rename:
|
||||||
|
if ret.Src, err = parseGit_StringWithSpace(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGitDiffIndexRawData(data io.ByteReader) (Data, error) {
|
||||||
|
ret := make([]GitDiffRawData, 0, 10)
|
||||||
|
for {
|
||||||
|
data, err := parseSingleGitDiffIndexRawData(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if data == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, *data)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
@@ -19,9 +19,7 @@ package common
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@@ -44,6 +42,11 @@ type GitDirectoryLister interface {
|
|||||||
GitDirectoryList(gitPath, commitId string) (dirlist map[string]string, err error)
|
GitDirectoryList(gitPath, commitId string) (dirlist map[string]string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GitSubmoduleFileConflictResolver interface {
|
||||||
|
GitResolveConflicts(cwd, MergeBase, Head, MergeHead string) error
|
||||||
|
GitResolveSubmoduleFileConflict(s GitStatusData, cwd, mergeBase, head, mergeHead string) error
|
||||||
|
}
|
||||||
|
|
||||||
type GitStatusLister interface {
|
type GitStatusLister interface {
|
||||||
GitStatus(cwd string) ([]GitStatusData, error)
|
GitStatus(cwd string) ([]GitStatusData, error)
|
||||||
}
|
}
|
||||||
@@ -75,6 +78,7 @@ type Git interface {
|
|||||||
GitExecQuietOrPanic(cwd string, params ...string)
|
GitExecQuietOrPanic(cwd string, params ...string)
|
||||||
|
|
||||||
GitDiffLister
|
GitDiffLister
|
||||||
|
GitSubmoduleFileConflictResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
type GitHandlerImpl struct {
|
type GitHandlerImpl struct {
|
||||||
@@ -350,20 +354,23 @@ var ExtraGitParams []string
|
|||||||
|
|
||||||
func (e *GitHandlerImpl) GitExecWithOutput(cwd string, params ...string) (string, error) {
|
func (e *GitHandlerImpl) GitExecWithOutput(cwd string, params ...string) (string, error) {
|
||||||
cmd := exec.Command("/usr/bin/git", params...)
|
cmd := exec.Command("/usr/bin/git", params...)
|
||||||
var identityFile string
|
|
||||||
if i := os.Getenv("AUTOGITS_IDENTITY_FILE"); len(i) > 0 {
|
|
||||||
identityFile = " -i " + i
|
|
||||||
}
|
|
||||||
cmd.Env = []string{
|
cmd.Env = []string{
|
||||||
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
|
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
|
||||||
"GIT_CONFIG_GLOBAL=/dev/null",
|
"GIT_CONFIG_GLOBAL=/dev/null",
|
||||||
"GIT_AUTHOR_NAME=" + e.GitCommiter,
|
|
||||||
"GIT_COMMITTER_NAME=" + e.GitCommiter,
|
|
||||||
"EMAIL=not@exist@src.opensuse.org",
|
|
||||||
"GIT_LFS_SKIP_SMUDGE=1",
|
"GIT_LFS_SKIP_SMUDGE=1",
|
||||||
"GIT_LFS_SKIP_PUSH=1",
|
"GIT_LFS_SKIP_PUSH=1",
|
||||||
"GIT_SSH_COMMAND=/usr/bin/ssh -o StrictHostKeyChecking=yes" + identityFile,
|
"GIT_SSH_COMMAND=/usr/bin/ssh -o StrictHostKeyChecking=yes",
|
||||||
}
|
}
|
||||||
|
if len(e.GitEmail) > 0 {
|
||||||
|
cmd.Env = append(cmd.Env, "EMAIL="+e.GitEmail)
|
||||||
|
}
|
||||||
|
if len(e.GitCommiter) > 0 {
|
||||||
|
cmd.Env = append(cmd.Env,
|
||||||
|
"GIT_AUTHOR_NAME="+e.GitCommiter,
|
||||||
|
"GIT_COMMITTER_NAME="+e.GitCommiter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if len(ExtraGitParams) > 0 {
|
if len(ExtraGitParams) > 0 {
|
||||||
cmd.Env = append(cmd.Env, ExtraGitParams...)
|
cmd.Env = append(cmd.Env, ExtraGitParams...)
|
||||||
}
|
}
|
||||||
@@ -1006,193 +1013,10 @@ func (e *GitHandlerImpl) GitSubmoduleCommitId(cwd, packageName, commitId string)
|
|||||||
return subCommitId, len(subCommitId) > 0
|
return subCommitId, len(subCommitId) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
func (e *GitHandlerImpl) GitExecWithDataParse(cwd string, dataprocessor func(io.ByteReader) (Data, error), gitcmd string, args ...string) (Data, error) {
|
||||||
GitStatus_Untracked = 0
|
LogDebug("getting", gitcmd)
|
||||||
GitStatus_Modified = 1
|
args = append([]string{gitcmd}, args...)
|
||||||
GitStatus_Ignored = 2
|
cmd := exec.Command("/usr/bin/git", args...)
|
||||||
GitStatus_Unmerged = 3 // States[0..3] -- Stage1, Stage2, Stage3 of merge objects
|
|
||||||
GitStatus_Renamed = 4 // orig name in States[0]
|
|
||||||
)
|
|
||||||
|
|
||||||
type GitStatusData struct {
|
|
||||||
Path string
|
|
||||||
Status int
|
|
||||||
States [3]string
|
|
||||||
|
|
||||||
/*
|
|
||||||
<sub> A 4 character field describing the submodule state.
|
|
||||||
"N..." when the entry is not a submodule.
|
|
||||||
"S<c><m><u>" when the entry is a submodule.
|
|
||||||
<c> is "C" if the commit changed; otherwise ".".
|
|
||||||
<m> is "M" if it has tracked changes; otherwise ".".
|
|
||||||
<u> is "U" if there are untracked changes; otherwise ".".
|
|
||||||
*/
|
|
||||||
SubmoduleChanges string
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseGitStatusHexString(data io.ByteReader) (string, error) {
|
|
||||||
str := make([]byte, 0, 32)
|
|
||||||
for {
|
|
||||||
c, err := data.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case c == 0 || c == ' ':
|
|
||||||
return string(str), nil
|
|
||||||
case c >= 'a' && c <= 'f':
|
|
||||||
case c >= 'A' && c <= 'F':
|
|
||||||
case c >= '0' && c <= '9':
|
|
||||||
default:
|
|
||||||
return "", errors.New("Invalid character in hex string:" + string(c))
|
|
||||||
}
|
|
||||||
str = append(str, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func parseGitStatusString(data io.ByteReader) (string, error) {
|
|
||||||
str := make([]byte, 0, 100)
|
|
||||||
for {
|
|
||||||
c, err := data.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.New("Unexpected EOF. Expected NUL string term")
|
|
||||||
}
|
|
||||||
if c == 0 || c == ' ' {
|
|
||||||
return string(str), nil
|
|
||||||
}
|
|
||||||
str = append(str, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseGitStatusStringWithSpace(data io.ByteReader) (string, error) {
|
|
||||||
str := make([]byte, 0, 100)
|
|
||||||
for {
|
|
||||||
c, err := data.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.New("Unexpected EOF. Expected NUL string term")
|
|
||||||
}
|
|
||||||
if c == 0 {
|
|
||||||
return string(str), nil
|
|
||||||
}
|
|
||||||
str = append(str, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func skipGitStatusEntry(data io.ByteReader, skipSpaceLen int) error {
|
|
||||||
for skipSpaceLen > 0 {
|
|
||||||
c, err := data.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c == ' ' {
|
|
||||||
skipSpaceLen--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSingleStatusEntry(data io.ByteReader) (*GitStatusData, error) {
|
|
||||||
ret := GitStatusData{}
|
|
||||||
statusType, err := data.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
switch statusType {
|
|
||||||
case '1':
|
|
||||||
var err error
|
|
||||||
if err = skipGitStatusEntry(data, 8); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret.Status = GitStatus_Modified
|
|
||||||
ret.Path, err = parseGitStatusStringWithSpace(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case '2':
|
|
||||||
var err error
|
|
||||||
if err = skipGitStatusEntry(data, 9); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret.Status = GitStatus_Renamed
|
|
||||||
ret.Path, err = parseGitStatusStringWithSpace(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret.States[0], err = parseGitStatusStringWithSpace(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case '?':
|
|
||||||
var err error
|
|
||||||
if err = skipGitStatusEntry(data, 1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret.Status = GitStatus_Untracked
|
|
||||||
ret.Path, err = parseGitStatusStringWithSpace(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case '!':
|
|
||||||
var err error
|
|
||||||
if err = skipGitStatusEntry(data, 1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret.Status = GitStatus_Ignored
|
|
||||||
ret.Path, err = parseGitStatusStringWithSpace(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case 'u':
|
|
||||||
var err error
|
|
||||||
if err = skipGitStatusEntry(data, 2); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ret.SubmoduleChanges, err = parseGitStatusString(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = skipGitStatusEntry(data, 4); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ret.States[0], err = parseGitStatusHexString(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ret.States[1], err = parseGitStatusHexString(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ret.States[2], err = parseGitStatusHexString(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret.Status = GitStatus_Unmerged
|
|
||||||
ret.Path, err = parseGitStatusStringWithSpace(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, errors.New("Invalid status type" + string(statusType))
|
|
||||||
}
|
|
||||||
return &ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseGitStatusData(data io.ByteReader) ([]GitStatusData, error) {
|
|
||||||
ret := make([]GitStatusData, 0, 10)
|
|
||||||
for {
|
|
||||||
data, err := parseSingleStatusEntry(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if data == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = append(ret, *data)
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *GitHandlerImpl) GitStatus(cwd string) (ret []GitStatusData, err error) {
|
|
||||||
LogDebug("getting git-status()")
|
|
||||||
|
|
||||||
cmd := exec.Command("/usr/bin/git", "status", "--porcelain=2", "-z")
|
|
||||||
cmd.Env = []string{
|
cmd.Env = []string{
|
||||||
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
|
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
|
||||||
"GIT_LFS_SKIP_SMUDGE=1",
|
"GIT_LFS_SKIP_SMUDGE=1",
|
||||||
@@ -1209,7 +1033,12 @@ func (e *GitHandlerImpl) GitStatus(cwd string) (ret []GitStatusData, err error)
|
|||||||
LogError("Error running command", cmd.Args, err)
|
LogError("Error running command", cmd.Args, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseGitStatusData(bufio.NewReader(bytes.NewReader(out)))
|
return dataprocessor(bytes.NewReader(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GitHandlerImpl) GitStatus(cwd string) (ret []GitStatusData, err error) {
|
||||||
|
data, err := e.GitExecWithDataParse(cwd, parseGitStatusData, "status", "--porcelain=2", "-z")
|
||||||
|
return data.([]GitStatusData), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *GitHandlerImpl) GitDiff(cwd, base, head string) (string, error) {
|
func (e *GitHandlerImpl) GitDiff(cwd, base, head string) (string, error) {
|
||||||
@@ -1234,3 +1063,122 @@ func (e *GitHandlerImpl) GitDiff(cwd, base, head string) (string, error) {
|
|||||||
|
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *GitHandlerImpl) GitDiffIndex(cwd, commit string) ([]GitDiffRawData, error) {
|
||||||
|
data, err := e.GitExecWithDataParse("diff-index", parseGitDiffIndexRawData, cwd, "diff-index", "-z", "--raw", "--full-index", "--submodule=short", "HEAD")
|
||||||
|
return data.([]GitDiffRawData), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (git *GitHandlerImpl) GitResolveConflicts(cwd, mergeBase, head, mergeHead string) error {
|
||||||
|
status, err := git.GitStatus(cwd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Status failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can only resolve conflicts with .gitmodules
|
||||||
|
for _, s := range status {
|
||||||
|
if s.Status == GitStatus_Unmerged && s.Path == ".gitmodules" {
|
||||||
|
if err := git.GitResolveSubmoduleFileConflict(s, cwd, mergeBase, head, mergeHead); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if s.Status == GitStatus_Unmerged {
|
||||||
|
return fmt.Errorf("Cannot automatically resolve conflict: %s", s.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return git.GitExec(cwd, "-c", "core.editor=true", "merge", "--continue")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (git *GitHandlerImpl) GitResolveSubmoduleFileConflict(s GitStatusData, cwd, mergeBase, head, mergeHead string) error {
|
||||||
|
submodules1, err := git.GitSubmoduleList(cwd, mergeBase)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to fetch submodules during merge resolution: %w", err)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
submodules2, err := git.GitSubmoduleList(cwd, head)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to fetch submodules during merge resolution: %w", err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
submodules3, err := git.GitSubmoduleList(cwd, mergeHead)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to fetch submodules during merge resolution: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find modified submodules in the mergeHead
|
||||||
|
modifiedSubmodules := make([]string, 0, 10)
|
||||||
|
removedSubmodules := make([]string, 0, 10)
|
||||||
|
addedSubmodules := make([]string, 0, 10)
|
||||||
|
|
||||||
|
for submodulePath, oldHash := range submodules1 {
|
||||||
|
if newHash, found := submodules3[submodulePath]; found && newHash != oldHash {
|
||||||
|
modifiedSubmodules = append(modifiedSubmodules, submodulePath)
|
||||||
|
} else if !found {
|
||||||
|
removedSubmodules = append(removedSubmodules, submodulePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for submodulePath, _ := range submodules3 {
|
||||||
|
if _, found := submodules1[submodulePath]; !found {
|
||||||
|
addedSubmodules = append(addedSubmodules, submodulePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to adjust the `submodules` list by the pending changes to the index
|
||||||
|
s1, err := git.GitExecWithOutput(cwd, "cat-file", "blob", s.States[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed fetching data during .gitmodules merge resoulution: %w", err)
|
||||||
|
}
|
||||||
|
s2, err := git.GitExecWithOutput(cwd, "cat-file", "blob", s.States[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed fetching data during .gitmodules merge resoulution: %w", err)
|
||||||
|
}
|
||||||
|
s3, err := git.GitExecWithOutput(cwd, "cat-file", "blob", s.States[2])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed fetching data during .gitmodules merge resoulution: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseSubmodulesFile(strings.NewReader(s1))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed parsing submodule file [%s] in merge: %w", s.States[0], err)
|
||||||
|
}
|
||||||
|
subs2, err := ParseSubmodulesFile(strings.NewReader(s2))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed parsing submodule file [%s] in merge: %w", s.States[0], err)
|
||||||
|
}
|
||||||
|
subs3, err := ParseSubmodulesFile(strings.NewReader(s3))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed parsing submodule file [%s] in merge: %w", s.States[0], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideSet := make([]Submodule, 0, len(addedSubmodules)+len(modifiedSubmodules))
|
||||||
|
for i := range subs3 {
|
||||||
|
if slices.Contains(addedSubmodules, subs3[i].Path) || slices.Contains(modifiedSubmodules, subs3[i].Path) {
|
||||||
|
overrideSet = append(overrideSet, subs3[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// merge from subs1 (merge-base), subs2 (changes in base since merge-base HEAD), subs3 (merge_source MERGE_HEAD)
|
||||||
|
// this will update submodules
|
||||||
|
SubmoduleCompare := func(a, b Submodule) int { return strings.Compare(a.Path, b.Path) }
|
||||||
|
CompactCompare := func(a, b Submodule) bool { return a.Path == b.Path }
|
||||||
|
|
||||||
|
// remove submodules that are removed in the PR
|
||||||
|
subs2 = slices.DeleteFunc(subs2, func(a Submodule) bool { return slices.Contains(removedSubmodules, a.Path) })
|
||||||
|
|
||||||
|
mergedSubs := slices.Concat(overrideSet, subs2)
|
||||||
|
slices.SortStableFunc(mergedSubs, SubmoduleCompare)
|
||||||
|
filteredSubs := slices.CompactFunc(mergedSubs, CompactCompare)
|
||||||
|
|
||||||
|
out, err := os.Create(path.Join(git.GetPath(), cwd, ".gitmodules"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't open .gitmodules for writing: %w", err)
|
||||||
|
}
|
||||||
|
if err = WriteSubmodules(filteredSubs, out); err != nil {
|
||||||
|
return fmt.Errorf("Can't write .gitmodules: %w", err)
|
||||||
|
}
|
||||||
|
if out.Close(); err != nil {
|
||||||
|
return fmt.Errorf("Can't close .gitmodules: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
git.GitExecOrPanic(cwd, "add", ".gitmodules")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"runtime/debug"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -93,6 +94,145 @@ func TestGitClone(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSubmoduleConflictResolution(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
checkout, merge string
|
||||||
|
result string
|
||||||
|
merge_fail bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "adding two submodules",
|
||||||
|
checkout: "base_add_b1",
|
||||||
|
merge: "base_add_b2",
|
||||||
|
result: `[submodule "pkgA"]
|
||||||
|
path = pkgA
|
||||||
|
url = ../pkgA
|
||||||
|
[submodule "pkgB"]
|
||||||
|
path = pkgB
|
||||||
|
url = ../pkgB
|
||||||
|
[submodule "pkgB1"]
|
||||||
|
path = pkgB1
|
||||||
|
url = ../pkgB1
|
||||||
|
[submodule "pkgB2"]
|
||||||
|
path = pkgB2
|
||||||
|
url = ../pkgB2
|
||||||
|
[submodule "pkgC"]
|
||||||
|
path = pkgC
|
||||||
|
url = ../pkgC
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove one module and add another",
|
||||||
|
checkout: "base_rm_c",
|
||||||
|
merge: "base_add_b2",
|
||||||
|
result: `[submodule "pkgA"]
|
||||||
|
path = pkgA
|
||||||
|
url = ../pkgA
|
||||||
|
[submodule "pkgB"]
|
||||||
|
path = pkgB
|
||||||
|
url = ../pkgB
|
||||||
|
[submodule "pkgB2"]
|
||||||
|
path = pkgB2
|
||||||
|
url = ../pkgB2
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add one and remove another",
|
||||||
|
checkout: "base_add_b2",
|
||||||
|
merge: "base_rm_c",
|
||||||
|
result: `[submodule "pkgA"]
|
||||||
|
path = pkgA
|
||||||
|
url = ../pkgA
|
||||||
|
[submodule "pkgB"]
|
||||||
|
path = pkgB
|
||||||
|
url = ../pkgB
|
||||||
|
[submodule "pkgB2"]
|
||||||
|
path = pkgB2
|
||||||
|
url = ../pkgB2
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rm modified submodule",
|
||||||
|
checkout: "base_modify_c",
|
||||||
|
merge: "base_rm_c",
|
||||||
|
merge_fail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "modified removed submodule",
|
||||||
|
checkout: "base_rm_c",
|
||||||
|
merge: "base_modify_c",
|
||||||
|
merge_fail: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := os.MkdirTemp(os.TempDir(), "submoduletests")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cmd := exec.Command(cwd + "/test_repo_setup.sh")
|
||||||
|
cmd.Dir = d
|
||||||
|
_, err = cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gh, err := AllocateGitWorkTree(d, "test", "foo@example.com")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
success := true
|
||||||
|
noErrorOrFail := func(t *testing.T, err error) {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(string(debug.Stack()), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
success = t.Run(test.name, func(t *testing.T) {
|
||||||
|
git, err := gh.ReadExistingPath("prjgit")
|
||||||
|
defer git.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
noErrorOrFail(t, git.GitExec("", "reset", "--hard"))
|
||||||
|
noErrorOrFail(t, git.GitExec("", "checkout", "-B", "test", test.checkout))
|
||||||
|
// noErrorOrFail(t, git.GitExec("", "merge", test.checkout))
|
||||||
|
err = git.GitExec("", "merge", test.merge)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected a conflict")
|
||||||
|
}
|
||||||
|
err = git.GitResolveConflicts("", "main", test.checkout, test.merge)
|
||||||
|
if err != nil {
|
||||||
|
if test.merge_fail {
|
||||||
|
return // success
|
||||||
|
}
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if test.merge_fail {
|
||||||
|
t.Fatal("Expected fail but succeeded?")
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile(git.GetPath() + "/.gitmodules")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read .gitmodules.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(data) != test.result {
|
||||||
|
t.Error("Expected", len(test.result), test.result, "but have", len(data), string(data))
|
||||||
|
}
|
||||||
|
}) && success
|
||||||
|
}
|
||||||
|
|
||||||
|
if success {
|
||||||
|
os.RemoveAll(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGitMsgParsing(t *testing.T) {
|
func TestGitMsgParsing(t *testing.T) {
|
||||||
t.Run("tree message with size 56", func(t *testing.T) {
|
t.Run("tree message with size 56", func(t *testing.T) {
|
||||||
const hdr = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98 tree 56\x00"
|
const hdr = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98 tree 56\x00"
|
||||||
@@ -584,12 +724,15 @@ func TestGitStatusParse(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(r) != len(test.res) {
|
|
||||||
t.Fatal("len(r):", len(r), "is not expected", len(test.res))
|
res := r.([]GitStatusData)
|
||||||
|
|
||||||
|
if len(res) != len(test.res) {
|
||||||
|
t.Fatal("len(r):", len(res), "is not expected", len(test.res))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, expected := range test.res {
|
for _, expected := range test.res {
|
||||||
if !slices.Contains(r, expected) {
|
if !slices.Contains(res, expected) {
|
||||||
t.Fatal("result", r, "doesn't contains expected", expected)
|
t.Fatal("result", r, "doesn't contains expected", expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
transport "github.com/go-openapi/runtime/client"
|
transport "github.com/go-openapi/runtime/client"
|
||||||
@@ -67,14 +66,6 @@ const (
|
|||||||
ReviewStateUnknown models.ReviewStateType = ""
|
ReviewStateUnknown models.ReviewStateType = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
type GiteaLabelGetter interface {
|
|
||||||
GetLabels(org, repo string, idx int64) ([]*models.Label, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type GiteaLabelSettter interface {
|
|
||||||
SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type GiteaTimelineFetcher interface {
|
type GiteaTimelineFetcher interface {
|
||||||
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
|
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
|
||||||
}
|
}
|
||||||
@@ -100,10 +91,9 @@ type GiteaPRUpdater interface {
|
|||||||
UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error)
|
UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiteaPRTimelineReviewFetcher interface {
|
type GiteaPRTimelineFetcher interface {
|
||||||
GiteaPRFetcher
|
GiteaPRFetcher
|
||||||
GiteaTimelineFetcher
|
GiteaTimelineFetcher
|
||||||
GiteaReviewFetcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiteaCommitFetcher interface {
|
type GiteaCommitFetcher interface {
|
||||||
@@ -129,16 +119,10 @@ type GiteaPRChecker interface {
|
|||||||
GiteaMaintainershipReader
|
GiteaMaintainershipReader
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiteaReviewFetcherAndRequesterAndUnrequester interface {
|
type GiteaReviewFetcherAndRequester interface {
|
||||||
GiteaReviewTimelineFetcher
|
GiteaReviewTimelineFetcher
|
||||||
GiteaCommentFetcher
|
GiteaCommentFetcher
|
||||||
GiteaReviewRequester
|
GiteaReviewRequester
|
||||||
GiteaReviewUnrequester
|
|
||||||
}
|
|
||||||
|
|
||||||
type GiteaUnreviewTimelineFetcher interface {
|
|
||||||
GiteaTimelineFetcher
|
|
||||||
GiteaReviewUnrequester
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GiteaReviewRequester interface {
|
type GiteaReviewRequester interface {
|
||||||
@@ -198,8 +182,6 @@ type Gitea interface {
|
|||||||
GiteaCommitStatusGetter
|
GiteaCommitStatusGetter
|
||||||
GiteaCommitStatusSetter
|
GiteaCommitStatusSetter
|
||||||
GiteaSetRepoOptions
|
GiteaSetRepoOptions
|
||||||
GiteaLabelGetter
|
|
||||||
GiteaLabelSettter
|
|
||||||
|
|
||||||
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
|
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
|
||||||
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
|
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
|
||||||
@@ -207,7 +189,7 @@ type Gitea interface {
|
|||||||
GetOrganization(orgName string) (*models.Organization, error)
|
GetOrganization(orgName string) (*models.Organization, error)
|
||||||
GetOrganizationRepositories(orgName string) ([]*models.Repository, error)
|
GetOrganizationRepositories(orgName string) ([]*models.Repository, error)
|
||||||
CreateRepositoryIfNotExist(git Git, org, repoName string) (*models.Repository, error)
|
CreateRepositoryIfNotExist(git Git, org, repoName string) (*models.Repository, error)
|
||||||
CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error, bool)
|
CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error)
|
||||||
GetPullRequestFileContent(pr *models.PullRequest, path string) ([]byte, string, error)
|
GetPullRequestFileContent(pr *models.PullRequest, path string) ([]byte, string, error)
|
||||||
GetRecentPullRequests(org, repo, branch string) ([]*models.PullRequest, error)
|
GetRecentPullRequests(org, repo, branch string) ([]*models.PullRequest, error)
|
||||||
GetRecentCommits(org, repo, branch string, commitNo int64) ([]*models.Commit, error)
|
GetRecentCommits(org, repo, branch string, commitNo int64) ([]*models.Commit, error)
|
||||||
@@ -484,30 +466,6 @@ func (gitea *GiteaTransport) SetRepoOptions(owner, repo string, manual_merge boo
|
|||||||
return ok.Payload, err
|
return ok.Payload, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gitea *GiteaTransport) GetLabels(owner, repo string, idx int64) ([]*models.Label, error) {
|
|
||||||
ret, err := gitea.client.Issue.IssueGetLabels(issue.NewIssueGetLabelsParams().WithOwner(owner).WithRepo(repo).WithIndex(idx), gitea.transport.DefaultAuthentication)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ret.Payload, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gitea *GiteaTransport) SetLabels(owner, repo string, idx int64, labels []string) ([]*models.Label, error) {
|
|
||||||
interfaceLabels := make([]interface{}, len(labels))
|
|
||||||
for i, l := range labels {
|
|
||||||
interfaceLabels[i] = l
|
|
||||||
}
|
|
||||||
|
|
||||||
ret, err := gitea.client.Issue.IssueAddLabel(issue.NewIssueAddLabelParams().WithOwner(owner).WithRepo(repo).WithIndex(idx).WithBody(&models.IssueLabelsOption{Labels: interfaceLabels}),
|
|
||||||
gitea.transport.DefaultAuthentication)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret.Payload, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GiteaNotificationType_Pull = "Pull"
|
GiteaNotificationType_Pull = "Pull"
|
||||||
)
|
)
|
||||||
@@ -685,7 +643,7 @@ func (gitea *GiteaTransport) CreateRepositoryIfNotExist(git Git, org, repoName s
|
|||||||
return repo.Payload, nil
|
return repo.Payload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error, bool) {
|
func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error) {
|
||||||
prOptions := models.CreatePullRequestOption{
|
prOptions := models.CreatePullRequestOption{
|
||||||
Base: targetId,
|
Base: targetId,
|
||||||
Head: srcId,
|
Head: srcId,
|
||||||
@@ -701,7 +659,7 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
|
|||||||
WithHead(srcId),
|
WithHead(srcId),
|
||||||
gitea.transport.DefaultAuthentication,
|
gitea.transport.DefaultAuthentication,
|
||||||
); err == nil && pr.Payload.State == "open" {
|
); err == nil && pr.Payload.State == "open" {
|
||||||
return pr.Payload, nil, false
|
return pr.Payload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, err := gitea.client.Repository.RepoCreatePullRequest(
|
pr, err := gitea.client.Repository.RepoCreatePullRequest(
|
||||||
@@ -715,10 +673,10 @@ func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Cannot create pull request. %w", err), true
|
return nil, fmt.Errorf("Cannot create pull request. %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pr.GetPayload(), nil, true
|
return pr.GetPayload(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gitea *GiteaTransport) RequestReviews(pr *models.PullRequest, reviewers ...string) ([]*models.PullReview, error) {
|
func (gitea *GiteaTransport) RequestReviews(pr *models.PullRequest, reviewers ...string) ([]*models.PullReview, error) {
|
||||||
@@ -805,79 +763,45 @@ func (gitea *GiteaTransport) AddComment(pr *models.PullRequest, comment string)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimelineCacheData struct {
|
|
||||||
data []*models.TimelineComment
|
|
||||||
lastCheck time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
var giteaTimelineCache map[string]TimelineCacheData = make(map[string]TimelineCacheData)
|
|
||||||
var giteaTimelineCacheMutex sync.RWMutex
|
|
||||||
|
|
||||||
// returns timeline in reverse chronological create order
|
|
||||||
func (gitea *GiteaTransport) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
func (gitea *GiteaTransport) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
||||||
page := int64(1)
|
page := int64(1)
|
||||||
resCount := 1
|
resCount := 1
|
||||||
|
|
||||||
prID := fmt.Sprintf("%s/%s!%d", org, repo, idx)
|
retData := []*models.TimelineComment{}
|
||||||
giteaTimelineCacheMutex.RLock()
|
|
||||||
TimelineCache, IsCached := giteaTimelineCache[prID]
|
|
||||||
var LastCachedTime strfmt.DateTime
|
|
||||||
if IsCached {
|
|
||||||
l := len(TimelineCache.data)
|
|
||||||
if l > 0 {
|
|
||||||
LastCachedTime = TimelineCache.data[0].Updated
|
|
||||||
}
|
|
||||||
|
|
||||||
// cache data for 5 seconds
|
|
||||||
if TimelineCache.lastCheck.Add(time.Second*5).Compare(time.Now()) > 0 {
|
|
||||||
giteaTimelineCacheMutex.RUnlock()
|
|
||||||
return TimelineCache.data, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
giteaTimelineCacheMutex.RUnlock()
|
|
||||||
|
|
||||||
giteaTimelineCacheMutex.Lock()
|
|
||||||
defer giteaTimelineCacheMutex.Unlock()
|
|
||||||
|
|
||||||
for resCount > 0 {
|
for resCount > 0 {
|
||||||
opts := issue.NewIssueGetCommentsAndTimelineParams().WithOwner(org).WithRepo(repo).WithIndex(idx).WithPage(&page)
|
res, err := gitea.client.Issue.IssueGetCommentsAndTimeline(
|
||||||
if !LastCachedTime.IsZero() {
|
issue.NewIssueGetCommentsAndTimelineParams().
|
||||||
opts = opts.WithSince(&LastCachedTime)
|
WithOwner(org).
|
||||||
}
|
WithRepo(repo).
|
||||||
res, err := gitea.client.Issue.IssueGetCommentsAndTimeline(opts, gitea.transport.DefaultAuthentication)
|
WithIndex(idx).
|
||||||
|
WithPage(&page),
|
||||||
|
gitea.transport.DefaultAuthentication,
|
||||||
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if resCount = len(res.Payload); resCount == 0 {
|
resCount = len(res.Payload)
|
||||||
break
|
LogDebug("page:", page, "len:", resCount)
|
||||||
}
|
if resCount == 0 {
|
||||||
|
|
||||||
for _, d := range res.Payload {
|
|
||||||
if d != nil {
|
|
||||||
if time.Time(d.Created).Compare(time.Time(LastCachedTime)) > 0 {
|
|
||||||
// created after last check, so we append here
|
|
||||||
TimelineCache.data = append(TimelineCache.data, d)
|
|
||||||
} else {
|
|
||||||
// we need something updated in the timeline, maybe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if resCount < 10 {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
page++
|
page++
|
||||||
|
|
||||||
|
for _, d := range res.Payload {
|
||||||
|
if d != nil {
|
||||||
|
retData = append(retData, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LogDebug("timeline", prID, "# timeline:", len(TimelineCache.data))
|
LogDebug("total results:", len(retData))
|
||||||
slices.SortFunc(TimelineCache.data, func(a, b *models.TimelineComment) int {
|
slices.SortFunc(retData, func(a, b *models.TimelineComment) int {
|
||||||
return time.Time(b.Created).Compare(time.Time(a.Created))
|
return time.Time(b.Created).Compare(time.Time(a.Created))
|
||||||
})
|
})
|
||||||
|
|
||||||
TimelineCache.lastCheck = time.Now()
|
return retData, nil
|
||||||
giteaTimelineCache[prID] = TimelineCache
|
|
||||||
|
|
||||||
return TimelineCache.data, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gitea *GiteaTransport) GetRepositoryFileContent(org, repo, hash, path string) ([]byte, string, error) {
|
func (gitea *GiteaTransport) GetRepositoryFileContent(org, repo, hash, path string) ([]byte, string, error) {
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
@@ -28,13 +26,11 @@ type MaintainershipMap struct {
|
|||||||
Data map[string][]string
|
Data map[string][]string
|
||||||
IsDir bool
|
IsDir bool
|
||||||
FetchPackage func(string) ([]byte, error)
|
FetchPackage func(string) ([]byte, error)
|
||||||
Raw []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseMaintainershipData(data []byte) (*MaintainershipMap, error) {
|
func parseMaintainershipData(data []byte) (*MaintainershipMap, error) {
|
||||||
maintainers := &MaintainershipMap{
|
maintainers := &MaintainershipMap{
|
||||||
Data: make(map[string][]string),
|
Data: make(map[string][]string),
|
||||||
Raw: data,
|
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, &maintainers.Data); err != nil {
|
if err := json.Unmarshal(data, &maintainers.Data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -63,7 +59,7 @@ func FetchProjectMaintainershipData(gitea GiteaMaintainershipReader, org, prjGit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := ParseMaintainershipData(data)
|
m, err := parseMaintainershipData(data)
|
||||||
if m != nil {
|
if m != nil {
|
||||||
m.IsDir = dir
|
m.IsDir = dir
|
||||||
m.FetchPackage = func(pkg string) ([]byte, error) {
|
m.FetchPackage = func(pkg string) ([]byte, error) {
|
||||||
@@ -168,135 +164,13 @@ func (data *MaintainershipMap) IsApproved(pkg string, reviews []*models.PullRevi
|
|||||||
return false
|
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 {
|
func (data *MaintainershipMap) WriteMaintainershipFile(writer io.StringWriter) error {
|
||||||
if data.IsDir {
|
if data.IsDir {
|
||||||
return fmt.Errorf("Not implemented")
|
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")
|
writer.WriteString("{\n")
|
||||||
|
|
||||||
if d, ok := data.Data[""]; ok {
|
if d, ok := data.Data[""]; ok {
|
||||||
eol := ","
|
eol := ","
|
||||||
if len(data.Data) == 1 {
|
if len(data.Data) == 1 {
|
||||||
@@ -307,12 +181,17 @@ func (data *MaintainershipMap) WriteMaintainershipFile(writer io.StringWriter) e
|
|||||||
writer.WriteString(fmt.Sprintf(" \"\": %s%s\n", string(str), eol))
|
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 {
|
for pkg := range data.Data {
|
||||||
if pkg == "" {
|
if pkg == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
keys = append(keys, pkg)
|
keys[i] = pkg
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if len(keys) >= i {
|
||||||
|
keys = slices.Delete(keys, i, len(keys))
|
||||||
}
|
}
|
||||||
slices.Sort(keys)
|
slices.Sort(keys)
|
||||||
for i, pkg := range keys {
|
for i, pkg := range keys {
|
||||||
|
|||||||
@@ -208,7 +208,6 @@ func TestMaintainershipFileWrite(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
is_dir bool
|
is_dir bool
|
||||||
maintainers map[string][]string
|
maintainers map[string][]string
|
||||||
raw []byte
|
|
||||||
expected_output string
|
expected_output string
|
||||||
expected_error error
|
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",
|
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 {
|
for _, test := range tests {
|
||||||
@@ -277,7 +239,6 @@ func TestMaintainershipFileWrite(t *testing.T) {
|
|||||||
data := common.MaintainershipMap{
|
data := common.MaintainershipMap{
|
||||||
Data: test.maintainers,
|
Data: test.maintainers,
|
||||||
IsDir: test.is_dir,
|
IsDir: test.is_dir,
|
||||||
Raw: test.raw,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := data.WriteMaintainershipFile(&b); err != test.expected_error {
|
if err := data.WriteMaintainershipFile(&b); err != test.expected_error {
|
||||||
@@ -287,7 +248,7 @@ func TestMaintainershipFileWrite(t *testing.T) {
|
|||||||
output := b.String()
|
output := b.String()
|
||||||
|
|
||||||
if test.expected_output != output {
|
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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,132 +18,6 @@ import (
|
|||||||
models "src.opensuse.org/autogits/common/gitea-generated/models"
|
models "src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockGiteaLabelGetter is a mock of GiteaLabelGetter interface.
|
|
||||||
type MockGiteaLabelGetter struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockGiteaLabelGetterMockRecorder
|
|
||||||
isgomock struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaLabelGetterMockRecorder is the mock recorder for MockGiteaLabelGetter.
|
|
||||||
type MockGiteaLabelGetterMockRecorder struct {
|
|
||||||
mock *MockGiteaLabelGetter
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockGiteaLabelGetter creates a new mock instance.
|
|
||||||
func NewMockGiteaLabelGetter(ctrl *gomock.Controller) *MockGiteaLabelGetter {
|
|
||||||
mock := &MockGiteaLabelGetter{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockGiteaLabelGetterMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockGiteaLabelGetter) EXPECT() *MockGiteaLabelGetterMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLabels mocks base method.
|
|
||||||
func (m *MockGiteaLabelGetter) GetLabels(org, repo string, idx int64) ([]*models.Label, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetLabels", org, repo, idx)
|
|
||||||
ret0, _ := ret[0].([]*models.Label)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLabels indicates an expected call of GetLabels.
|
|
||||||
func (mr *MockGiteaLabelGetterMockRecorder) GetLabels(org, repo, idx any) *MockGiteaLabelGetterGetLabelsCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLabels", reflect.TypeOf((*MockGiteaLabelGetter)(nil).GetLabels), org, repo, idx)
|
|
||||||
return &MockGiteaLabelGetterGetLabelsCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaLabelGetterGetLabelsCall wrap *gomock.Call
|
|
||||||
type MockGiteaLabelGetterGetLabelsCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaLabelGetterGetLabelsCall) Return(arg0 []*models.Label, arg1 error) *MockGiteaLabelGetterGetLabelsCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaLabelGetterGetLabelsCall) Do(f func(string, string, int64) ([]*models.Label, error)) *MockGiteaLabelGetterGetLabelsCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaLabelGetterGetLabelsCall) DoAndReturn(f func(string, string, int64) ([]*models.Label, error)) *MockGiteaLabelGetterGetLabelsCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaLabelSettter is a mock of GiteaLabelSettter interface.
|
|
||||||
type MockGiteaLabelSettter struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockGiteaLabelSettterMockRecorder
|
|
||||||
isgomock struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaLabelSettterMockRecorder is the mock recorder for MockGiteaLabelSettter.
|
|
||||||
type MockGiteaLabelSettterMockRecorder struct {
|
|
||||||
mock *MockGiteaLabelSettter
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockGiteaLabelSettter creates a new mock instance.
|
|
||||||
func NewMockGiteaLabelSettter(ctrl *gomock.Controller) *MockGiteaLabelSettter {
|
|
||||||
mock := &MockGiteaLabelSettter{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockGiteaLabelSettterMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockGiteaLabelSettter) EXPECT() *MockGiteaLabelSettterMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLabels mocks base method.
|
|
||||||
func (m *MockGiteaLabelSettter) SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "SetLabels", org, repo, idx, labels)
|
|
||||||
ret0, _ := ret[0].([]*models.Label)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLabels indicates an expected call of SetLabels.
|
|
||||||
func (mr *MockGiteaLabelSettterMockRecorder) SetLabels(org, repo, idx, labels any) *MockGiteaLabelSettterSetLabelsCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLabels", reflect.TypeOf((*MockGiteaLabelSettter)(nil).SetLabels), org, repo, idx, labels)
|
|
||||||
return &MockGiteaLabelSettterSetLabelsCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaLabelSettterSetLabelsCall wrap *gomock.Call
|
|
||||||
type MockGiteaLabelSettterSetLabelsCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaLabelSettterSetLabelsCall) Return(arg0 []*models.Label, arg1 error) *MockGiteaLabelSettterSetLabelsCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaLabelSettterSetLabelsCall) Do(f func(string, string, int64, []string) ([]*models.Label, error)) *MockGiteaLabelSettterSetLabelsCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaLabelSettterSetLabelsCall) DoAndReturn(f func(string, string, int64, []string) ([]*models.Label, error)) *MockGiteaLabelSettterSetLabelsCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaTimelineFetcher is a mock of GiteaTimelineFetcher interface.
|
// MockGiteaTimelineFetcher is a mock of GiteaTimelineFetcher interface.
|
||||||
type MockGiteaTimelineFetcher struct {
|
type MockGiteaTimelineFetcher struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
@@ -562,32 +436,32 @@ func (c *MockGiteaPRUpdaterUpdatePullRequestCall) DoAndReturn(f func(string, str
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaPRTimelineReviewFetcher is a mock of GiteaPRTimelineReviewFetcher interface.
|
// MockGiteaPRTimelineFetcher is a mock of GiteaPRTimelineFetcher interface.
|
||||||
type MockGiteaPRTimelineReviewFetcher struct {
|
type MockGiteaPRTimelineFetcher struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
recorder *MockGiteaPRTimelineReviewFetcherMockRecorder
|
recorder *MockGiteaPRTimelineFetcherMockRecorder
|
||||||
isgomock struct{}
|
isgomock struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaPRTimelineReviewFetcherMockRecorder is the mock recorder for MockGiteaPRTimelineReviewFetcher.
|
// MockGiteaPRTimelineFetcherMockRecorder is the mock recorder for MockGiteaPRTimelineFetcher.
|
||||||
type MockGiteaPRTimelineReviewFetcherMockRecorder struct {
|
type MockGiteaPRTimelineFetcherMockRecorder struct {
|
||||||
mock *MockGiteaPRTimelineReviewFetcher
|
mock *MockGiteaPRTimelineFetcher
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockGiteaPRTimelineReviewFetcher creates a new mock instance.
|
// NewMockGiteaPRTimelineFetcher creates a new mock instance.
|
||||||
func NewMockGiteaPRTimelineReviewFetcher(ctrl *gomock.Controller) *MockGiteaPRTimelineReviewFetcher {
|
func NewMockGiteaPRTimelineFetcher(ctrl *gomock.Controller) *MockGiteaPRTimelineFetcher {
|
||||||
mock := &MockGiteaPRTimelineReviewFetcher{ctrl: ctrl}
|
mock := &MockGiteaPRTimelineFetcher{ctrl: ctrl}
|
||||||
mock.recorder = &MockGiteaPRTimelineReviewFetcherMockRecorder{mock}
|
mock.recorder = &MockGiteaPRTimelineFetcherMockRecorder{mock}
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
func (m *MockGiteaPRTimelineReviewFetcher) EXPECT() *MockGiteaPRTimelineReviewFetcherMockRecorder {
|
func (m *MockGiteaPRTimelineFetcher) EXPECT() *MockGiteaPRTimelineFetcherMockRecorder {
|
||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequest mocks base method.
|
// GetPullRequest mocks base method.
|
||||||
func (m *MockGiteaPRTimelineReviewFetcher) GetPullRequest(org, project string, num int64) (*models.PullRequest, error) {
|
func (m *MockGiteaPRTimelineFetcher) GetPullRequest(org, project string, num int64) (*models.PullRequest, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetPullRequest", org, project, num)
|
ret := m.ctrl.Call(m, "GetPullRequest", org, project, num)
|
||||||
ret0, _ := ret[0].(*models.PullRequest)
|
ret0, _ := ret[0].(*models.PullRequest)
|
||||||
@@ -596,76 +470,37 @@ func (m *MockGiteaPRTimelineReviewFetcher) GetPullRequest(org, project string, n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequest indicates an expected call of GetPullRequest.
|
// GetPullRequest indicates an expected call of GetPullRequest.
|
||||||
func (mr *MockGiteaPRTimelineReviewFetcherMockRecorder) GetPullRequest(org, project, num any) *MockGiteaPRTimelineReviewFetcherGetPullRequestCall {
|
func (mr *MockGiteaPRTimelineFetcherMockRecorder) GetPullRequest(org, project, num any) *MockGiteaPRTimelineFetcherGetPullRequestCall {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequest", reflect.TypeOf((*MockGiteaPRTimelineReviewFetcher)(nil).GetPullRequest), org, project, num)
|
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequest", reflect.TypeOf((*MockGiteaPRTimelineFetcher)(nil).GetPullRequest), org, project, num)
|
||||||
return &MockGiteaPRTimelineReviewFetcherGetPullRequestCall{Call: call}
|
return &MockGiteaPRTimelineFetcherGetPullRequestCall{Call: call}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaPRTimelineReviewFetcherGetPullRequestCall wrap *gomock.Call
|
// MockGiteaPRTimelineFetcherGetPullRequestCall wrap *gomock.Call
|
||||||
type MockGiteaPRTimelineReviewFetcherGetPullRequestCall struct {
|
type MockGiteaPRTimelineFetcherGetPullRequestCall struct {
|
||||||
*gomock.Call
|
*gomock.Call
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetPullRequestCall) Return(arg0 *models.PullRequest, arg1 error) *MockGiteaPRTimelineReviewFetcherGetPullRequestCall {
|
func (c *MockGiteaPRTimelineFetcherGetPullRequestCall) Return(arg0 *models.PullRequest, arg1 error) *MockGiteaPRTimelineFetcherGetPullRequestCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetPullRequestCall) Do(f func(string, string, int64) (*models.PullRequest, error)) *MockGiteaPRTimelineReviewFetcherGetPullRequestCall {
|
func (c *MockGiteaPRTimelineFetcherGetPullRequestCall) Do(f func(string, string, int64) (*models.PullRequest, error)) *MockGiteaPRTimelineFetcherGetPullRequestCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetPullRequestCall) DoAndReturn(f func(string, string, int64) (*models.PullRequest, error)) *MockGiteaPRTimelineReviewFetcherGetPullRequestCall {
|
func (c *MockGiteaPRTimelineFetcherGetPullRequestCall) DoAndReturn(f func(string, string, int64) (*models.PullRequest, error)) *MockGiteaPRTimelineFetcherGetPullRequestCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPullRequestReviews mocks base method.
|
|
||||||
func (m *MockGiteaPRTimelineReviewFetcher) GetPullRequestReviews(org, project string, PRnum int64) ([]*models.PullReview, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetPullRequestReviews", org, project, PRnum)
|
|
||||||
ret0, _ := ret[0].([]*models.PullReview)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPullRequestReviews indicates an expected call of GetPullRequestReviews.
|
|
||||||
func (mr *MockGiteaPRTimelineReviewFetcherMockRecorder) GetPullRequestReviews(org, project, PRnum any) *MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequestReviews", reflect.TypeOf((*MockGiteaPRTimelineReviewFetcher)(nil).GetPullRequestReviews), org, project, PRnum)
|
|
||||||
return &MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall wrap *gomock.Call
|
|
||||||
type MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall) Return(arg0 []*models.PullReview, arg1 error) *MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall) Do(f func(string, string, int64) ([]*models.PullReview, error)) *MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall) DoAndReturn(f func(string, string, int64) ([]*models.PullReview, error)) *MockGiteaPRTimelineReviewFetcherGetPullRequestReviewsCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTimeline mocks base method.
|
// GetTimeline mocks base method.
|
||||||
func (m *MockGiteaPRTimelineReviewFetcher) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
func (m *MockGiteaPRTimelineFetcher) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetTimeline", org, repo, idx)
|
ret := m.ctrl.Call(m, "GetTimeline", org, repo, idx)
|
||||||
ret0, _ := ret[0].([]*models.TimelineComment)
|
ret0, _ := ret[0].([]*models.TimelineComment)
|
||||||
@@ -674,31 +509,31 @@ func (m *MockGiteaPRTimelineReviewFetcher) GetTimeline(org, repo string, idx int
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTimeline indicates an expected call of GetTimeline.
|
// GetTimeline indicates an expected call of GetTimeline.
|
||||||
func (mr *MockGiteaPRTimelineReviewFetcherMockRecorder) GetTimeline(org, repo, idx any) *MockGiteaPRTimelineReviewFetcherGetTimelineCall {
|
func (mr *MockGiteaPRTimelineFetcherMockRecorder) GetTimeline(org, repo, idx any) *MockGiteaPRTimelineFetcherGetTimelineCall {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeline", reflect.TypeOf((*MockGiteaPRTimelineReviewFetcher)(nil).GetTimeline), org, repo, idx)
|
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeline", reflect.TypeOf((*MockGiteaPRTimelineFetcher)(nil).GetTimeline), org, repo, idx)
|
||||||
return &MockGiteaPRTimelineReviewFetcherGetTimelineCall{Call: call}
|
return &MockGiteaPRTimelineFetcherGetTimelineCall{Call: call}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaPRTimelineReviewFetcherGetTimelineCall wrap *gomock.Call
|
// MockGiteaPRTimelineFetcherGetTimelineCall wrap *gomock.Call
|
||||||
type MockGiteaPRTimelineReviewFetcherGetTimelineCall struct {
|
type MockGiteaPRTimelineFetcherGetTimelineCall struct {
|
||||||
*gomock.Call
|
*gomock.Call
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetTimelineCall) Return(arg0 []*models.TimelineComment, arg1 error) *MockGiteaPRTimelineReviewFetcherGetTimelineCall {
|
func (c *MockGiteaPRTimelineFetcherGetTimelineCall) Return(arg0 []*models.TimelineComment, arg1 error) *MockGiteaPRTimelineFetcherGetTimelineCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetTimelineCall) Do(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaPRTimelineReviewFetcherGetTimelineCall {
|
func (c *MockGiteaPRTimelineFetcherGetTimelineCall) Do(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaPRTimelineFetcherGetTimelineCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockGiteaPRTimelineReviewFetcherGetTimelineCall) DoAndReturn(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaPRTimelineReviewFetcherGetTimelineCall {
|
func (c *MockGiteaPRTimelineFetcherGetTimelineCall) DoAndReturn(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaPRTimelineFetcherGetTimelineCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -1215,32 +1050,32 @@ func (c *MockGiteaPRCheckerGetTimelineCall) DoAndReturn(f func(string, string, i
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaReviewFetcherAndRequesterAndUnrequester is a mock of GiteaReviewFetcherAndRequesterAndUnrequester interface.
|
// MockGiteaReviewFetcherAndRequester is a mock of GiteaReviewFetcherAndRequester interface.
|
||||||
type MockGiteaReviewFetcherAndRequesterAndUnrequester struct {
|
type MockGiteaReviewFetcherAndRequester struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
recorder *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder
|
recorder *MockGiteaReviewFetcherAndRequesterMockRecorder
|
||||||
isgomock struct{}
|
isgomock struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder is the mock recorder for MockGiteaReviewFetcherAndRequesterAndUnrequester.
|
// MockGiteaReviewFetcherAndRequesterMockRecorder is the mock recorder for MockGiteaReviewFetcherAndRequester.
|
||||||
type MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder struct {
|
type MockGiteaReviewFetcherAndRequesterMockRecorder struct {
|
||||||
mock *MockGiteaReviewFetcherAndRequesterAndUnrequester
|
mock *MockGiteaReviewFetcherAndRequester
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockGiteaReviewFetcherAndRequesterAndUnrequester creates a new mock instance.
|
// NewMockGiteaReviewFetcherAndRequester creates a new mock instance.
|
||||||
func NewMockGiteaReviewFetcherAndRequesterAndUnrequester(ctrl *gomock.Controller) *MockGiteaReviewFetcherAndRequesterAndUnrequester {
|
func NewMockGiteaReviewFetcherAndRequester(ctrl *gomock.Controller) *MockGiteaReviewFetcherAndRequester {
|
||||||
mock := &MockGiteaReviewFetcherAndRequesterAndUnrequester{ctrl: ctrl}
|
mock := &MockGiteaReviewFetcherAndRequester{ctrl: ctrl}
|
||||||
mock.recorder = &MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder{mock}
|
mock.recorder = &MockGiteaReviewFetcherAndRequesterMockRecorder{mock}
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) EXPECT() *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder {
|
func (m *MockGiteaReviewFetcherAndRequester) EXPECT() *MockGiteaReviewFetcherAndRequesterMockRecorder {
|
||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIssueComments mocks base method.
|
// GetIssueComments mocks base method.
|
||||||
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error) {
|
func (m *MockGiteaReviewFetcherAndRequester) GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetIssueComments", org, project, issueNo)
|
ret := m.ctrl.Call(m, "GetIssueComments", org, project, issueNo)
|
||||||
ret0, _ := ret[0].([]*models.Comment)
|
ret0, _ := ret[0].([]*models.Comment)
|
||||||
@@ -1249,37 +1084,37 @@ func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) GetIssueComments(org,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetIssueComments indicates an expected call of GetIssueComments.
|
// GetIssueComments indicates an expected call of GetIssueComments.
|
||||||
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) GetIssueComments(org, project, issueNo any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall {
|
func (mr *MockGiteaReviewFetcherAndRequesterMockRecorder) GetIssueComments(org, project, issueNo any) *MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssueComments", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).GetIssueComments), org, project, issueNo)
|
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIssueComments", reflect.TypeOf((*MockGiteaReviewFetcherAndRequester)(nil).GetIssueComments), org, project, issueNo)
|
||||||
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall{Call: call}
|
return &MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall{Call: call}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall wrap *gomock.Call
|
// MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall wrap *gomock.Call
|
||||||
type MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall struct {
|
type MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall struct {
|
||||||
*gomock.Call
|
*gomock.Call
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall) Return(arg0 []*models.Comment, arg1 error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall) Return(arg0 []*models.Comment, arg1 error) *MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall) Do(f func(string, string, int64) ([]*models.Comment, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall) Do(f func(string, string, int64) ([]*models.Comment, error)) *MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall) DoAndReturn(f func(string, string, int64) ([]*models.Comment, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetIssueCommentsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall) DoAndReturn(f func(string, string, int64) ([]*models.Comment, error)) *MockGiteaReviewFetcherAndRequesterGetIssueCommentsCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequestReviews mocks base method.
|
// GetPullRequestReviews mocks base method.
|
||||||
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) GetPullRequestReviews(org, project string, PRnum int64) ([]*models.PullReview, error) {
|
func (m *MockGiteaReviewFetcherAndRequester) GetPullRequestReviews(org, project string, PRnum int64) ([]*models.PullReview, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetPullRequestReviews", org, project, PRnum)
|
ret := m.ctrl.Call(m, "GetPullRequestReviews", org, project, PRnum)
|
||||||
ret0, _ := ret[0].([]*models.PullReview)
|
ret0, _ := ret[0].([]*models.PullReview)
|
||||||
@@ -1288,37 +1123,37 @@ func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) GetPullRequestReviews
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequestReviews indicates an expected call of GetPullRequestReviews.
|
// GetPullRequestReviews indicates an expected call of GetPullRequestReviews.
|
||||||
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) GetPullRequestReviews(org, project, PRnum any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall {
|
func (mr *MockGiteaReviewFetcherAndRequesterMockRecorder) GetPullRequestReviews(org, project, PRnum any) *MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequestReviews", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).GetPullRequestReviews), org, project, PRnum)
|
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPullRequestReviews", reflect.TypeOf((*MockGiteaReviewFetcherAndRequester)(nil).GetPullRequestReviews), org, project, PRnum)
|
||||||
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall{Call: call}
|
return &MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall{Call: call}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall wrap *gomock.Call
|
// MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall wrap *gomock.Call
|
||||||
type MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall struct {
|
type MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall struct {
|
||||||
*gomock.Call
|
*gomock.Call
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall) Return(arg0 []*models.PullReview, arg1 error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall) Return(arg0 []*models.PullReview, arg1 error) *MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall) Do(f func(string, string, int64) ([]*models.PullReview, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall) Do(f func(string, string, int64) ([]*models.PullReview, error)) *MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall) DoAndReturn(f func(string, string, int64) ([]*models.PullReview, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetPullRequestReviewsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall) DoAndReturn(f func(string, string, int64) ([]*models.PullReview, error)) *MockGiteaReviewFetcherAndRequesterGetPullRequestReviewsCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTimeline mocks base method.
|
// GetTimeline mocks base method.
|
||||||
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
func (m *MockGiteaReviewFetcherAndRequester) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetTimeline", org, repo, idx)
|
ret := m.ctrl.Call(m, "GetTimeline", org, repo, idx)
|
||||||
ret0, _ := ret[0].([]*models.TimelineComment)
|
ret0, _ := ret[0].([]*models.TimelineComment)
|
||||||
@@ -1327,37 +1162,37 @@ func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) GetTimeline(org, repo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTimeline indicates an expected call of GetTimeline.
|
// GetTimeline indicates an expected call of GetTimeline.
|
||||||
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) GetTimeline(org, repo, idx any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall {
|
func (mr *MockGiteaReviewFetcherAndRequesterMockRecorder) GetTimeline(org, repo, idx any) *MockGiteaReviewFetcherAndRequesterGetTimelineCall {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeline", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).GetTimeline), org, repo, idx)
|
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeline", reflect.TypeOf((*MockGiteaReviewFetcherAndRequester)(nil).GetTimeline), org, repo, idx)
|
||||||
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall{Call: call}
|
return &MockGiteaReviewFetcherAndRequesterGetTimelineCall{Call: call}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall wrap *gomock.Call
|
// MockGiteaReviewFetcherAndRequesterGetTimelineCall wrap *gomock.Call
|
||||||
type MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall struct {
|
type MockGiteaReviewFetcherAndRequesterGetTimelineCall struct {
|
||||||
*gomock.Call
|
*gomock.Call
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall) Return(arg0 []*models.TimelineComment, arg1 error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetTimelineCall) Return(arg0 []*models.TimelineComment, arg1 error) *MockGiteaReviewFetcherAndRequesterGetTimelineCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall) Do(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetTimelineCall) Do(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaReviewFetcherAndRequesterGetTimelineCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall) DoAndReturn(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterGetTimelineCall {
|
func (c *MockGiteaReviewFetcherAndRequesterGetTimelineCall) DoAndReturn(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaReviewFetcherAndRequesterGetTimelineCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestReviews mocks base method.
|
// RequestReviews mocks base method.
|
||||||
func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) RequestReviews(pr *models.PullRequest, reviewer ...string) ([]*models.PullReview, error) {
|
func (m *MockGiteaReviewFetcherAndRequester) RequestReviews(pr *models.PullRequest, reviewer ...string) ([]*models.PullReview, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
varargs := []any{pr}
|
varargs := []any{pr}
|
||||||
for _, a := range reviewer {
|
for _, a := range reviewer {
|
||||||
@@ -1370,181 +1205,32 @@ func (m *MockGiteaReviewFetcherAndRequesterAndUnrequester) RequestReviews(pr *mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RequestReviews indicates an expected call of RequestReviews.
|
// RequestReviews indicates an expected call of RequestReviews.
|
||||||
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) RequestReviews(pr any, reviewer ...any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall {
|
func (mr *MockGiteaReviewFetcherAndRequesterMockRecorder) RequestReviews(pr any, reviewer ...any) *MockGiteaReviewFetcherAndRequesterRequestReviewsCall {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
varargs := append([]any{pr}, reviewer...)
|
varargs := append([]any{pr}, reviewer...)
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestReviews", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).RequestReviews), varargs...)
|
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestReviews", reflect.TypeOf((*MockGiteaReviewFetcherAndRequester)(nil).RequestReviews), varargs...)
|
||||||
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall{Call: call}
|
return &MockGiteaReviewFetcherAndRequesterRequestReviewsCall{Call: call}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall wrap *gomock.Call
|
// MockGiteaReviewFetcherAndRequesterRequestReviewsCall wrap *gomock.Call
|
||||||
type MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall struct {
|
type MockGiteaReviewFetcherAndRequesterRequestReviewsCall struct {
|
||||||
*gomock.Call
|
*gomock.Call
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall) Return(arg0 []*models.PullReview, arg1 error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterRequestReviewsCall) Return(arg0 []*models.PullReview, arg1 error) *MockGiteaReviewFetcherAndRequesterRequestReviewsCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall) Do(f func(*models.PullRequest, ...string) ([]*models.PullReview, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterRequestReviewsCall) Do(f func(*models.PullRequest, ...string) ([]*models.PullReview, error)) *MockGiteaReviewFetcherAndRequesterRequestReviewsCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall) DoAndReturn(f func(*models.PullRequest, ...string) ([]*models.PullReview, error)) *MockGiteaReviewFetcherAndRequesterAndUnrequesterRequestReviewsCall {
|
func (c *MockGiteaReviewFetcherAndRequesterRequestReviewsCall) DoAndReturn(f func(*models.PullRequest, ...string) ([]*models.PullReview, error)) *MockGiteaReviewFetcherAndRequesterRequestReviewsCall {
|
||||||
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()
|
|
||||||
varargs := []any{org, repo, id}
|
|
||||||
for _, a := range reviwers {
|
|
||||||
varargs = append(varargs, a)
|
|
||||||
}
|
|
||||||
ret := m.ctrl.Call(m, "UnrequestReview", varargs...)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnrequestReview indicates an expected call of UnrequestReview.
|
|
||||||
func (mr *MockGiteaReviewFetcherAndRequesterAndUnrequesterMockRecorder) UnrequestReview(org, repo, id any, reviwers ...any) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
varargs := append([]any{org, repo, id}, reviwers...)
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnrequestReview", reflect.TypeOf((*MockGiteaReviewFetcherAndRequesterAndUnrequester)(nil).UnrequestReview), varargs...)
|
|
||||||
return &MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall wrap *gomock.Call
|
|
||||||
type MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall) Return(arg0 error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall {
|
|
||||||
c.Call = c.Call.Return(arg0)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall) Do(f func(string, string, int64, ...string) error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall) DoAndReturn(f func(string, string, int64, ...string) error) *MockGiteaReviewFetcherAndRequesterAndUnrequesterUnrequestReviewCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaUnreviewTimelineFetcher is a mock of GiteaUnreviewTimelineFetcher interface.
|
|
||||||
type MockGiteaUnreviewTimelineFetcher struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockGiteaUnreviewTimelineFetcherMockRecorder
|
|
||||||
isgomock struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaUnreviewTimelineFetcherMockRecorder is the mock recorder for MockGiteaUnreviewTimelineFetcher.
|
|
||||||
type MockGiteaUnreviewTimelineFetcherMockRecorder struct {
|
|
||||||
mock *MockGiteaUnreviewTimelineFetcher
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockGiteaUnreviewTimelineFetcher creates a new mock instance.
|
|
||||||
func NewMockGiteaUnreviewTimelineFetcher(ctrl *gomock.Controller) *MockGiteaUnreviewTimelineFetcher {
|
|
||||||
mock := &MockGiteaUnreviewTimelineFetcher{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockGiteaUnreviewTimelineFetcherMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockGiteaUnreviewTimelineFetcher) EXPECT() *MockGiteaUnreviewTimelineFetcherMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTimeline mocks base method.
|
|
||||||
func (m *MockGiteaUnreviewTimelineFetcher) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetTimeline", org, repo, idx)
|
|
||||||
ret0, _ := ret[0].([]*models.TimelineComment)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTimeline indicates an expected call of GetTimeline.
|
|
||||||
func (mr *MockGiteaUnreviewTimelineFetcherMockRecorder) GetTimeline(org, repo, idx any) *MockGiteaUnreviewTimelineFetcherGetTimelineCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeline", reflect.TypeOf((*MockGiteaUnreviewTimelineFetcher)(nil).GetTimeline), org, repo, idx)
|
|
||||||
return &MockGiteaUnreviewTimelineFetcherGetTimelineCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaUnreviewTimelineFetcherGetTimelineCall wrap *gomock.Call
|
|
||||||
type MockGiteaUnreviewTimelineFetcherGetTimelineCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherGetTimelineCall) Return(arg0 []*models.TimelineComment, arg1 error) *MockGiteaUnreviewTimelineFetcherGetTimelineCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherGetTimelineCall) Do(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaUnreviewTimelineFetcherGetTimelineCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherGetTimelineCall) DoAndReturn(f func(string, string, int64) ([]*models.TimelineComment, error)) *MockGiteaUnreviewTimelineFetcherGetTimelineCall {
|
|
||||||
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()
|
|
||||||
varargs := []any{org, repo, id}
|
|
||||||
for _, a := range reviwers {
|
|
||||||
varargs = append(varargs, a)
|
|
||||||
}
|
|
||||||
ret := m.ctrl.Call(m, "UnrequestReview", varargs...)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnrequestReview indicates an expected call of UnrequestReview.
|
|
||||||
func (mr *MockGiteaUnreviewTimelineFetcherMockRecorder) UnrequestReview(org, repo, id any, reviwers ...any) *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
varargs := append([]any{org, repo, id}, reviwers...)
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnrequestReview", reflect.TypeOf((*MockGiteaUnreviewTimelineFetcher)(nil).UnrequestReview), varargs...)
|
|
||||||
return &MockGiteaUnreviewTimelineFetcherUnrequestReviewCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaUnreviewTimelineFetcherUnrequestReviewCall wrap *gomock.Call
|
|
||||||
type MockGiteaUnreviewTimelineFetcherUnrequestReviewCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall) Return(arg0 error) *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall {
|
|
||||||
c.Call = c.Call.Return(arg0)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall) Do(f func(string, string, int64, ...string) error) *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall) DoAndReturn(f func(string, string, int64, ...string) error) *MockGiteaUnreviewTimelineFetcherUnrequestReviewCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -2164,13 +1850,12 @@ func (c *MockGiteaAddReviewCommentCall) DoAndReturn(f func(*models.PullRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreatePullRequestIfNotExist mocks base method.
|
// CreatePullRequestIfNotExist mocks base method.
|
||||||
func (m *MockGitea) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error, bool) {
|
func (m *MockGitea) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "CreatePullRequestIfNotExist", repo, srcId, targetId, title, body)
|
ret := m.ctrl.Call(m, "CreatePullRequestIfNotExist", repo, srcId, targetId, title, body)
|
||||||
ret0, _ := ret[0].(*models.PullRequest)
|
ret0, _ := ret[0].(*models.PullRequest)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
ret2, _ := ret[2].(bool)
|
return ret0, ret1
|
||||||
return ret0, ret1, ret2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreatePullRequestIfNotExist indicates an expected call of CreatePullRequestIfNotExist.
|
// CreatePullRequestIfNotExist indicates an expected call of CreatePullRequestIfNotExist.
|
||||||
@@ -2186,19 +1871,19 @@ type MockGiteaCreatePullRequestIfNotExistCall struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockGiteaCreatePullRequestIfNotExistCall) Return(arg0 *models.PullRequest, arg1 error, arg2 bool) *MockGiteaCreatePullRequestIfNotExistCall {
|
func (c *MockGiteaCreatePullRequestIfNotExistCall) Return(arg0 *models.PullRequest, arg1 error) *MockGiteaCreatePullRequestIfNotExistCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1, arg2)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockGiteaCreatePullRequestIfNotExistCall) Do(f func(*models.Repository, string, string, string, string) (*models.PullRequest, error, bool)) *MockGiteaCreatePullRequestIfNotExistCall {
|
func (c *MockGiteaCreatePullRequestIfNotExistCall) Do(f func(*models.Repository, string, string, string, string) (*models.PullRequest, error)) *MockGiteaCreatePullRequestIfNotExistCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockGiteaCreatePullRequestIfNotExistCall) DoAndReturn(f func(*models.Repository, string, string, string, string) (*models.PullRequest, error, bool)) *MockGiteaCreatePullRequestIfNotExistCall {
|
func (c *MockGiteaCreatePullRequestIfNotExistCall) DoAndReturn(f func(*models.Repository, string, string, string, string) (*models.PullRequest, error)) *MockGiteaCreatePullRequestIfNotExistCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -2517,45 +2202,6 @@ func (c *MockGiteaGetIssueCommentsCall) DoAndReturn(f func(string, string, int64
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabels mocks base method.
|
|
||||||
func (m *MockGitea) GetLabels(org, repo string, idx int64) ([]*models.Label, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetLabels", org, repo, idx)
|
|
||||||
ret0, _ := ret[0].([]*models.Label)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLabels indicates an expected call of GetLabels.
|
|
||||||
func (mr *MockGiteaMockRecorder) GetLabels(org, repo, idx any) *MockGiteaGetLabelsCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLabels", reflect.TypeOf((*MockGitea)(nil).GetLabels), org, repo, idx)
|
|
||||||
return &MockGiteaGetLabelsCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaGetLabelsCall wrap *gomock.Call
|
|
||||||
type MockGiteaGetLabelsCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaGetLabelsCall) Return(arg0 []*models.Label, arg1 error) *MockGiteaGetLabelsCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaGetLabelsCall) Do(f func(string, string, int64) ([]*models.Label, error)) *MockGiteaGetLabelsCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaGetLabelsCall) DoAndReturn(f func(string, string, int64) ([]*models.Label, error)) *MockGiteaGetLabelsCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNotifications mocks base method.
|
// GetNotifications mocks base method.
|
||||||
func (m *MockGitea) GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error) {
|
func (m *MockGitea) GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@@ -3147,45 +2793,6 @@ func (c *MockGiteaSetCommitStatusCall) DoAndReturn(f func(string, string, string
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLabels mocks base method.
|
|
||||||
func (m *MockGitea) SetLabels(org, repo string, idx int64, labels []string) ([]*models.Label, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "SetLabels", org, repo, idx, labels)
|
|
||||||
ret0, _ := ret[0].([]*models.Label)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLabels indicates an expected call of SetLabels.
|
|
||||||
func (mr *MockGiteaMockRecorder) SetLabels(org, repo, idx, labels any) *MockGiteaSetLabelsCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLabels", reflect.TypeOf((*MockGitea)(nil).SetLabels), org, repo, idx, labels)
|
|
||||||
return &MockGiteaSetLabelsCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockGiteaSetLabelsCall wrap *gomock.Call
|
|
||||||
type MockGiteaSetLabelsCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockGiteaSetLabelsCall) Return(arg0 []*models.Label, arg1 error) *MockGiteaSetLabelsCall {
|
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockGiteaSetLabelsCall) Do(f func(string, string, int64, []string) ([]*models.Label, error)) *MockGiteaSetLabelsCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockGiteaSetLabelsCall) DoAndReturn(f func(string, string, int64, []string) ([]*models.Label, error)) *MockGiteaSetLabelsCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNotificationRead mocks base method.
|
// SetNotificationRead mocks base method.
|
||||||
func (m *MockGitea) SetNotificationRead(notificationId int64) error {
|
func (m *MockGitea) SetNotificationRead(notificationId int64) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
329
common/pr.go
329
common/pr.go
@@ -4,8 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -23,8 +21,7 @@ type PRSet struct {
|
|||||||
PRs []*PRInfo
|
PRs []*PRInfo
|
||||||
Config *AutogitConfig
|
Config *AutogitConfig
|
||||||
|
|
||||||
BotUser string
|
BotUser string
|
||||||
HasAutoStaging bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (prinfo *PRInfo) PRComponents() (org string, repo string, idx int64) {
|
func (prinfo *PRInfo) PRComponents() (org string, repo string, idx int64) {
|
||||||
@@ -34,41 +31,6 @@ func (prinfo *PRInfo) PRComponents() (org string, repo string, idx int64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (prinfo *PRInfo) RemoveReviewers(gitea GiteaUnreviewTimelineFetcher, Reviewers []string, BotUser string) {
|
|
||||||
org, repo, idx := prinfo.PRComponents()
|
|
||||||
tl, err := gitea.GetTimeline(org, repo, idx)
|
|
||||||
if err != nil {
|
|
||||||
LogError("Failed to fetch timeline for", PRtoString(prinfo.PR), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// find review request for each reviewer
|
|
||||||
ReviewersToUnrequest := Reviewers
|
|
||||||
ReviewersAlreadyChecked := []string{}
|
|
||||||
|
|
||||||
for _, tlc := range tl {
|
|
||||||
if tlc.Type == TimelineCommentType_ReviewRequested && tlc.Assignee != nil {
|
|
||||||
user := tlc.Assignee.UserName
|
|
||||||
|
|
||||||
if idx := slices.Index(ReviewersToUnrequest, user); idx >= 0 && !slices.Contains(ReviewersAlreadyChecked, user) {
|
|
||||||
if tlc.User != nil && tlc.User.UserName == BotUser {
|
|
||||||
ReviewersAlreadyChecked = append(ReviewersAlreadyChecked, user)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ReviewersToUnrequest = slices.Delete(ReviewersToUnrequest, idx, idx+1)
|
|
||||||
if len(Reviewers) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogDebug("Unrequesting reviewes for", PRtoString(prinfo.PR), ReviewersToUnrequest)
|
|
||||||
err = gitea.UnrequestReview(org, repo, idx, ReviewersToUnrequest...)
|
|
||||||
if err != nil {
|
|
||||||
LogError("Failed to unrequest reviewers for", PRtoString(prinfo.PR), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readPRData(gitea GiteaPRFetcher, pr *models.PullRequest, currentSet []*PRInfo, config *AutogitConfig) ([]*PRInfo, error) {
|
func readPRData(gitea GiteaPRFetcher, pr *models.PullRequest, currentSet []*PRInfo, config *AutogitConfig) ([]*PRInfo, error) {
|
||||||
for _, p := range currentSet {
|
for _, p := range currentSet {
|
||||||
if pr.Index == p.PR.Index && pr.Base.Repo.Name == p.PR.Base.Repo.Name && pr.Base.Repo.Owner.UserName == p.PR.Base.Repo.Owner.UserName {
|
if pr.Index == p.PR.Index && pr.Base.Repo.Name == p.PR.Base.Repo.Name && pr.Base.Repo.Owner.UserName == p.PR.Base.Repo.Owner.UserName {
|
||||||
@@ -99,7 +61,7 @@ func readPRData(gitea GiteaPRFetcher, pr *models.PullRequest, currentSet []*PRIn
|
|||||||
|
|
||||||
var Timeline_RefIssueNotFound error = errors.New("RefIssue not found on the timeline")
|
var Timeline_RefIssueNotFound error = errors.New("RefIssue not found on the timeline")
|
||||||
|
|
||||||
func LastPrjGitRefOnTimeline(botUser string, gitea GiteaPRTimelineReviewFetcher, org, repo string, num int64, config *AutogitConfig) (*models.PullRequest, error) {
|
func LastPrjGitRefOnTimeline(botUser string, gitea GiteaPRTimelineFetcher, org, repo string, num int64, config *AutogitConfig) (*models.PullRequest, error) {
|
||||||
timeline, err := gitea.GetTimeline(org, repo, num)
|
timeline, err := gitea.GetTimeline(org, repo, num)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError("Failed to fetch timeline for", org, repo, "#", num, err)
|
LogError("Failed to fetch timeline for", org, repo, "#", num, err)
|
||||||
@@ -124,19 +86,14 @@ func LastPrjGitRefOnTimeline(botUser string, gitea GiteaPRTimelineReviewFetcher,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pr, err := gitea.GetPullRequest(prjGitOrg, prjGitRepo, issue.Index)
|
pr, err := gitea.GetPullRequest(prjGitOrg, prjGitRepo, issue.Index)
|
||||||
if err != nil {
|
switch err.(type) {
|
||||||
switch err.(type) {
|
case *repository.RepoGetPullRequestNotFound: // deleted?
|
||||||
case *repository.RepoGetPullRequestNotFound: // deleted?
|
continue
|
||||||
continue
|
default:
|
||||||
default:
|
LogDebug("PrjGit RefIssue fetch error from timeline", issue.Index, err)
|
||||||
LogDebug("PrjGit RefIssue fetch error from timeline", issue.Index, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebug("found ref PR on timeline:", PRtoString(pr))
|
if pr.Base.Ref != prjGitBranch {
|
||||||
if pr.Base.Name != prjGitBranch {
|
|
||||||
LogDebug(" -> not matching:", pr.Base.Name, prjGitBranch)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +113,7 @@ func LastPrjGitRefOnTimeline(botUser string, gitea GiteaPRTimelineReviewFetcher,
|
|||||||
return nil, Timeline_RefIssueNotFound
|
return nil, Timeline_RefIssueNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func FetchPRSet(user string, gitea GiteaPRTimelineReviewFetcher, org, repo string, num int64, config *AutogitConfig) (*PRSet, error) {
|
func FetchPRSet(user string, gitea GiteaPRTimelineFetcher, org, repo string, num int64, config *AutogitConfig) (*PRSet, error) {
|
||||||
var pr *models.PullRequest
|
var pr *models.PullRequest
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -182,15 +139,6 @@ func FetchPRSet(user string, gitea GiteaPRTimelineReviewFetcher, org, repo strin
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pr := range prs {
|
|
||||||
org, repo, idx := pr.PRComponents()
|
|
||||||
reviews, err := FetchGiteaReviews(gitea, org, repo, idx)
|
|
||||||
if err != nil {
|
|
||||||
LogError("Error fetching reviews for", PRtoString(pr.PR), ":", err)
|
|
||||||
}
|
|
||||||
pr.Reviews = reviews
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PRSet{
|
return &PRSet{
|
||||||
PRs: prs,
|
PRs: prs,
|
||||||
Config: config,
|
Config: config,
|
||||||
@@ -198,12 +146,6 @@ func FetchPRSet(user string, gitea GiteaPRTimelineReviewFetcher, org, repo strin
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (prset *PRSet) RemoveReviewers(gitea GiteaUnreviewTimelineFetcher, reviewers []string) {
|
|
||||||
for _, prinfo := range prset.PRs {
|
|
||||||
prinfo.RemoveReviewers(gitea, reviewers, prset.BotUser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *PRSet) Find(pr *models.PullRequest) (*PRInfo, bool) {
|
func (rs *PRSet) Find(pr *models.PullRequest) (*PRInfo, bool) {
|
||||||
for _, p := range rs.PRs {
|
for _, p := range rs.PRs {
|
||||||
if p.PR.Base.RepoID == pr.Base.RepoID &&
|
if p.PR.Base.RepoID == pr.Base.RepoID &&
|
||||||
@@ -289,144 +231,67 @@ next_rs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, pr := range prjpr_set {
|
for _, pr := range prjpr_set {
|
||||||
if strings.EqualFold(prinfo.PR.Base.Repo.Owner.UserName, pr.Org) && strings.EqualFold(prinfo.PR.Base.Repo.Name, pr.Repo) && prinfo.PR.Index == pr.Num {
|
if prinfo.PR.Base.Repo.Owner.UserName == pr.Org && prinfo.PR.Base.Repo.Name == pr.Repo && prinfo.PR.Index == pr.Num {
|
||||||
continue next_rs
|
continue next_rs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LogDebug(" PR: ", PRtoString(prinfo.PR), "not found in project git PRSet")
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *PRSet) FindMissingAndExtraReviewers(maintainers MaintainershipData, idx int) (missing, extra []string) {
|
func (rs *PRSet) AssignReviewers(gitea GiteaReviewFetcherAndRequester, maintainers MaintainershipData) error {
|
||||||
configReviewers := ParseReviewers(rs.Config.Reviewers)
|
configReviewers := ParseReviewers(rs.Config.Reviewers)
|
||||||
|
|
||||||
// remove reviewers that were already requested and are not stale
|
for _, pr := range rs.PRs {
|
||||||
prjMaintainers := maintainers.ListProjectMaintainers(nil)
|
reviewers := []string{}
|
||||||
LogDebug("project maintainers:", prjMaintainers)
|
|
||||||
|
|
||||||
pr := rs.PRs[idx]
|
if rs.IsPrjGitPR(pr.PR) {
|
||||||
if rs.IsPrjGitPR(pr.PR) {
|
reviewers = slices.Concat(configReviewers.Prj, configReviewers.PrjOptional)
|
||||||
missing = slices.Concat(configReviewers.Prj, configReviewers.PrjOptional)
|
LogDebug("PrjGit submitter:", pr.PR.User.UserName)
|
||||||
if rs.HasAutoStaging {
|
if len(rs.PRs) == 1 {
|
||||||
missing = append(missing, Bot_BuildReview)
|
reviewers = slices.Concat(reviewers, maintainers.ListProjectMaintainers(nil))
|
||||||
}
|
}
|
||||||
LogDebug("PrjGit submitter:", pr.PR.User.UserName)
|
|
||||||
// only need project maintainer reviews if:
|
|
||||||
// * not created by a bot and has other PRs, or
|
|
||||||
// * not created by maintainer
|
|
||||||
noReviewPRCreators := prjMaintainers
|
|
||||||
if len(rs.PRs) > 1 {
|
|
||||||
noReviewPRCreators = append(noReviewPRCreators, rs.BotUser)
|
|
||||||
}
|
|
||||||
if slices.Contains(noReviewPRCreators, pr.PR.User.UserName) || pr.Reviews.IsReviewedByOneOf(prjMaintainers...) {
|
|
||||||
LogDebug("Project already reviewed by a project maintainer, remove rest")
|
|
||||||
// do not remove reviewers if they are also maintainers
|
|
||||||
prjMaintainers = slices.DeleteFunc(prjMaintainers, func(m string) bool { return slices.Contains(missing, m) })
|
|
||||||
extra = slices.Concat(prjMaintainers, []string{rs.BotUser})
|
|
||||||
} else {
|
} else {
|
||||||
// if bot not created PrjGit or prj maintainer, we need to add project reviewers here
|
pkg := pr.PR.Base.Repo.Name
|
||||||
if slices.Contains(noReviewPRCreators, pr.PR.User.UserName) {
|
reviewers = slices.Concat(configReviewers.Pkg, maintainers.ListProjectMaintainers(nil), maintainers.ListPackageMaintainers(pkg, nil), configReviewers.PkgOptional)
|
||||||
LogDebug("No need for project maintainers")
|
}
|
||||||
extra = slices.Concat(prjMaintainers, []string{rs.BotUser})
|
|
||||||
|
slices.Sort(reviewers)
|
||||||
|
reviewers = slices.Compact(reviewers)
|
||||||
|
|
||||||
|
// submitters do not need to review their own work
|
||||||
|
if idx := slices.Index(reviewers, pr.PR.User.UserName); idx != -1 {
|
||||||
|
reviewers = slices.Delete(reviewers, idx, idx+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
LogDebug("PR: ", pr.PR.Base.Repo.Name, pr.PR.Index)
|
||||||
|
LogDebug("reviewers for PR:", reviewers)
|
||||||
|
|
||||||
|
// remove reviewers that were already requested and are not stale
|
||||||
|
reviews, err := FetchGiteaReviews(gitea, reviewers, pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index)
|
||||||
|
if err != nil {
|
||||||
|
LogError("Error fetching reviews:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx := 0; idx < len(reviewers); {
|
||||||
|
user := reviewers[idx]
|
||||||
|
if reviews.HasPendingReviewBy(user) || reviews.IsReviewedBy(user) {
|
||||||
|
reviewers = slices.Delete(reviewers, idx, idx+1)
|
||||||
|
LogDebug("removing reviewer:", user)
|
||||||
} else {
|
} else {
|
||||||
LogDebug("Adding prjMaintainers to PrjGit")
|
idx++
|
||||||
missing = append(missing, prjMaintainers...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pkg := pr.PR.Base.Repo.Name
|
|
||||||
pkgMaintainers := maintainers.ListPackageMaintainers(pkg, nil)
|
|
||||||
Maintainers := slices.Concat(prjMaintainers, pkgMaintainers)
|
|
||||||
noReviewPkgPRCreators := pkgMaintainers
|
|
||||||
|
|
||||||
LogDebug("packakge maintainers:", Maintainers)
|
// get maintainers associated with the PR too
|
||||||
|
if len(reviewers) > 0 {
|
||||||
missing = slices.Concat(configReviewers.Pkg, configReviewers.PkgOptional)
|
LogDebug("Requesting reviews from:", reviewers)
|
||||||
if slices.Contains(noReviewPkgPRCreators, pr.PR.User.UserName) || pr.Reviews.IsReviewedByOneOf(Maintainers...) {
|
|
||||||
// submitter is maintainer or already reviewed
|
|
||||||
LogDebug("Package reviewed by maintainer (or subitter is maintainer), remove the rest of them")
|
|
||||||
// do not remove reviewers if they are also maintainers
|
|
||||||
Maintainers = slices.DeleteFunc(Maintainers, func(m string) bool { return slices.Contains(missing, m) })
|
|
||||||
extra = slices.Concat(Maintainers, []string{rs.BotUser})
|
|
||||||
} else {
|
|
||||||
// maintainer review is missing
|
|
||||||
LogDebug("Adding package maintainers to package git")
|
|
||||||
missing = append(missing, pkgMaintainers...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
slices.Sort(missing)
|
|
||||||
missing = slices.Compact(missing)
|
|
||||||
|
|
||||||
slices.Sort(extra)
|
|
||||||
extra = slices.Compact(extra)
|
|
||||||
|
|
||||||
// submitters cannot review their own work
|
|
||||||
if idx := slices.Index(missing, pr.PR.User.UserName); idx != -1 {
|
|
||||||
missing = slices.Delete(missing, idx, idx+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
LogDebug("PR: ", PRtoString(pr.PR))
|
|
||||||
LogDebug(" preliminary add reviewers for PR:", missing)
|
|
||||||
LogDebug(" preliminary rm reviewers for PR:", extra)
|
|
||||||
|
|
||||||
// remove missing reviewers that are already done or already pending
|
|
||||||
for idx := 0; idx < len(missing); {
|
|
||||||
user := missing[idx]
|
|
||||||
if pr.Reviews.HasPendingReviewBy(user) || pr.Reviews.IsReviewedBy(user) {
|
|
||||||
missing = slices.Delete(missing, idx, idx+1)
|
|
||||||
LogDebug(" removing done/pending reviewer:", user)
|
|
||||||
} else {
|
|
||||||
idx++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove extra reviews that are actually only pending, and only pending by us
|
|
||||||
for idx := 0; idx < len(extra); {
|
|
||||||
user := extra[idx]
|
|
||||||
rr := pr.Reviews.FindReviewRequester(user)
|
|
||||||
if rr != nil && rr.User.UserName == rs.BotUser && pr.Reviews.HasPendingReviewBy(user) {
|
|
||||||
// good to remove this review
|
|
||||||
idx++
|
|
||||||
} else {
|
|
||||||
// this review should not be considered as extra by us
|
|
||||||
LogDebug(" - cannot find? to remove", user)
|
|
||||||
if rr != nil {
|
|
||||||
LogDebug(" ", rr.User.UserName, "vs.", rs.BotUser, pr.Reviews.HasPendingReviewBy(user))
|
|
||||||
}
|
|
||||||
extra = slices.Delete(extra, idx, idx+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogDebug(" add reviewers for PR:", missing)
|
|
||||||
LogDebug(" rm reviewers for PR:", extra)
|
|
||||||
|
|
||||||
return missing, extra
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *PRSet) AssignReviewers(gitea GiteaReviewFetcherAndRequesterAndUnrequester, maintainers MaintainershipData) error {
|
|
||||||
for idx, pr := range rs.PRs {
|
|
||||||
missingReviewers, extraReviewers := rs.FindMissingAndExtraReviewers(maintainers, idx)
|
|
||||||
|
|
||||||
if len(missingReviewers) > 0 {
|
|
||||||
LogDebug(" Requesting reviews from:", missingReviewers)
|
|
||||||
if !IsDryRun {
|
if !IsDryRun {
|
||||||
for _, r := range missingReviewers {
|
for _, r := range reviewers {
|
||||||
if _, err := gitea.RequestReviews(pr.PR, r); err != nil {
|
if _, err := gitea.RequestReviews(pr.PR, r); err != nil {
|
||||||
LogError("Cannot create reviews on", PRtoString(pr.PR), "for user:", r, err)
|
LogError("Cannot create reviews on", fmt.Sprintf("%s/%s!%d for [%s]", pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index, strings.Join(reviewers, ", ")), err)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(extraReviewers) > 0 {
|
|
||||||
LogDebug(" UnRequesting reviews from:", extraReviewers)
|
|
||||||
if !IsDryRun {
|
|
||||||
for _, r := range extraReviewers {
|
|
||||||
org, repo, idx := pr.PRComponents()
|
|
||||||
if err := gitea.UnrequestReview(org, repo, idx, r); err != nil {
|
|
||||||
LogError("Cannot unrequest reviews on", PRtoString(pr.PR), "for user:", r, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,12 +317,11 @@ func (rs *PRSet) IsApproved(gitea GiteaPRChecker, maintainers MaintainershipData
|
|||||||
if err == nil && prjgit != nil {
|
if err == nil && prjgit != nil {
|
||||||
reviewers := slices.Concat(configReviewers.Prj, maintainers.ListProjectMaintainers(groups))
|
reviewers := slices.Concat(configReviewers.Prj, maintainers.ListProjectMaintainers(groups))
|
||||||
LogDebug("Fetching reviews for", prjgit.PR.Base.Repo.Owner.UserName, prjgit.PR.Base.Repo.Name, prjgit.PR.Index)
|
LogDebug("Fetching reviews for", prjgit.PR.Base.Repo.Owner.UserName, prjgit.PR.Base.Repo.Name, prjgit.PR.Index)
|
||||||
r, err := FetchGiteaReviews(gitea, prjgit.PR.Base.Repo.Owner.UserName, prjgit.PR.Base.Repo.Name, prjgit.PR.Index)
|
r, err := FetchGiteaReviews(gitea, reviewers, prjgit.PR.Base.Repo.Owner.UserName, prjgit.PR.Base.Repo.Name, prjgit.PR.Index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError("Cannot fetch gita reaviews for PR:", err)
|
LogError("Cannot fetch gita reaviews for PR:", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
r.RequestedReviewers = reviewers
|
|
||||||
prjgit.Reviews = r
|
prjgit.Reviews = r
|
||||||
if prjgit.Reviews.IsManualMergeOK() {
|
if prjgit.Reviews.IsManualMergeOK() {
|
||||||
is_manually_reviewed_ok = true
|
is_manually_reviewed_ok = true
|
||||||
@@ -473,12 +337,11 @@ func (rs *PRSet) IsApproved(gitea GiteaPRChecker, maintainers MaintainershipData
|
|||||||
pkg := pr.PR.Base.Repo.Name
|
pkg := pr.PR.Base.Repo.Name
|
||||||
reviewers := slices.Concat(configReviewers.Pkg, maintainers.ListPackageMaintainers(pkg, groups))
|
reviewers := slices.Concat(configReviewers.Pkg, maintainers.ListPackageMaintainers(pkg, groups))
|
||||||
LogDebug("Fetching reviews for", pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index)
|
LogDebug("Fetching reviews for", pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index)
|
||||||
r, err := FetchGiteaReviews(gitea, pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index)
|
r, err := FetchGiteaReviews(gitea, reviewers, pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError("Cannot fetch gita reaviews for PR:", err)
|
LogError("Cannot fetch gita reaviews for PR:", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
r.RequestedReviewers = reviewers
|
|
||||||
pr.Reviews = r
|
pr.Reviews = r
|
||||||
if !pr.Reviews.IsManualMergeOK() {
|
if !pr.Reviews.IsManualMergeOK() {
|
||||||
LogInfo("Not approved manual merge. PR:", pr.PR.URL)
|
LogInfo("Not approved manual merge. PR:", pr.PR.URL)
|
||||||
@@ -500,9 +363,6 @@ func (rs *PRSet) IsApproved(gitea GiteaPRChecker, maintainers MaintainershipData
|
|||||||
var pkg string
|
var pkg string
|
||||||
if rs.IsPrjGitPR(pr.PR) {
|
if rs.IsPrjGitPR(pr.PR) {
|
||||||
reviewers = configReviewers.Prj
|
reviewers = configReviewers.Prj
|
||||||
if rs.HasAutoStaging {
|
|
||||||
reviewers = append(reviewers, Bot_BuildReview)
|
|
||||||
}
|
|
||||||
pkg = ""
|
pkg = ""
|
||||||
} else {
|
} else {
|
||||||
reviewers = configReviewers.Pkg
|
reviewers = configReviewers.Pkg
|
||||||
@@ -514,12 +374,11 @@ func (rs *PRSet) IsApproved(gitea GiteaPRChecker, maintainers MaintainershipData
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := FetchGiteaReviews(gitea, pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index)
|
r, err := FetchGiteaReviews(gitea, reviewers, pr.PR.Base.Repo.Owner.UserName, pr.PR.Base.Repo.Name, pr.PR.Index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError("Cannot fetch gitea reaviews for PR:", err)
|
LogError("Cannot fetch gitea reaviews for PR:", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
r.RequestedReviewers = reviewers
|
|
||||||
|
|
||||||
is_manually_reviewed_ok = r.IsApproved()
|
is_manually_reviewed_ok = r.IsApproved()
|
||||||
LogDebug("PR to", pr.PR.Base.Repo.Name, "reviewed?", is_manually_reviewed_ok)
|
LogDebug("PR to", pr.PR.Base.Repo.Name, "reviewed?", is_manually_reviewed_ok)
|
||||||
@@ -532,7 +391,7 @@ func (rs *PRSet) IsApproved(gitea GiteaPRChecker, maintainers MaintainershipData
|
|||||||
|
|
||||||
if need_maintainer_review := !rs.IsPrjGitPR(pr.PR) || pr.PR.User.UserName != rs.BotUser; need_maintainer_review {
|
if need_maintainer_review := !rs.IsPrjGitPR(pr.PR) || pr.PR.User.UserName != rs.BotUser; need_maintainer_review {
|
||||||
// Do not expand groups here, as the group-review-bot will ACK if group has reviewed.
|
// Do not expand groups here, as the group-review-bot will ACK if group has reviewed.
|
||||||
if is_manually_reviewed_ok = maintainers.IsApproved(pkg, r.Reviews, pr.PR.User.UserName, nil); !is_manually_reviewed_ok {
|
if is_manually_reviewed_ok = maintainers.IsApproved(pkg, r.reviews, pr.PR.User.UserName, nil); !is_manually_reviewed_ok {
|
||||||
LogDebug(" not approved?", pkg)
|
LogDebug(" not approved?", pkg)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -572,80 +431,8 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequester, git Git) error {
|
|||||||
|
|
||||||
err = git.GitExec(DefaultGitPrj, "merge", "--no-ff", "-m", msg, prjgit.Head.Sha)
|
err = git.GitExec(DefaultGitPrj, "merge", "--no-ff", "-m", msg, prjgit.Head.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status, statusErr := git.GitStatus(DefaultGitPrj)
|
if resolveError := git.GitResolveConflicts(DefaultGitPrj, prjgit.MergeBase, prjgit.Base.Sha, prjgit.Head.Sha); resolveError != nil {
|
||||||
if statusErr != nil {
|
return fmt.Errorf("Merge failed. (%w): %w", err, resolveError)
|
||||||
return fmt.Errorf("Failed to merge: %w . Status also failed: %w", err, statusErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// we can only resolve conflicts with .gitmodules
|
|
||||||
for _, s := range status {
|
|
||||||
if s.Status == GitStatus_Unmerged {
|
|
||||||
panic("Can't handle conflicts yet")
|
|
||||||
if s.Path != ".gitmodules" {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
submodules, err := git.GitSubmoduleList(DefaultGitPrj, "MERGE_HEAD")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to fetch submodules during merge resolution: %w", err)
|
|
||||||
}
|
|
||||||
s1, err := git.GitExecWithOutput(DefaultGitPrj, "cat-file", "blob", s.States[0])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed fetching data during .gitmodules merge resoulution: %w", err)
|
|
||||||
}
|
|
||||||
s2, err := git.GitExecWithOutput(DefaultGitPrj, "cat-file", "blob", s.States[1])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed fetching data during .gitmodules merge resoulution: %w", err)
|
|
||||||
}
|
|
||||||
s3, err := git.GitExecWithOutput(DefaultGitPrj, "cat-file", "blob", s.States[2])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed fetching data during .gitmodules merge resoulution: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
subs1, err := ParseSubmodulesFile(strings.NewReader(s1))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed parsing submodule file [%s] in merge: %w", s.States[0], err)
|
|
||||||
}
|
|
||||||
subs2, err := ParseSubmodulesFile(strings.NewReader(s2))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed parsing submodule file [%s] in merge: %w", s.States[0], err)
|
|
||||||
}
|
|
||||||
subs3, err := ParseSubmodulesFile(strings.NewReader(s3))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed parsing submodule file [%s] in merge: %w", s.States[0], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// merge from subs3 (target), subs1 (orig), subs2 (2-nd base that is missing from target base)
|
|
||||||
// this will update submodules
|
|
||||||
mergedSubs := slices.Concat(subs1, subs2, subs3)
|
|
||||||
|
|
||||||
var filteredSubs []Submodule = make([]Submodule, 0, max(len(subs1), len(subs2), len(subs3)))
|
|
||||||
nextSub:
|
|
||||||
for subName := range submodules {
|
|
||||||
|
|
||||||
for i := range mergedSubs {
|
|
||||||
if path.Base(mergedSubs[i].Path) == subName {
|
|
||||||
filteredSubs = append(filteredSubs, mergedSubs[i])
|
|
||||||
continue nextSub
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("Cannot find submodule for path: %s", subName)
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := os.Create(path.Join(git.GetPath(), DefaultGitPrj, ".gitmodules"))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Can't open .gitmodules for writing: %w", err)
|
|
||||||
}
|
|
||||||
if err = WriteSubmodules(filteredSubs, out); err != nil {
|
|
||||||
return fmt.Errorf("Can't write .gitmodules: %w", err)
|
|
||||||
}
|
|
||||||
if out.Close(); err != nil {
|
|
||||||
return fmt.Errorf("Can't close .gitmodules: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
git.GitExecOrPanic(DefaultGitPrj, "add", ".gitmodules")
|
|
||||||
git.GitExecOrPanic(DefaultGitPrj, "-c", "core.editor=true", "merge", "--continue")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package common_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
@@ -75,7 +76,7 @@ func TestPR(t *testing.T) {
|
|||||||
consistentSet bool
|
consistentSet bool
|
||||||
prjGitPRIndex int
|
prjGitPRIndex int
|
||||||
|
|
||||||
reviewSetFetcher func(*mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error)
|
reviewSetFetcher func(*mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Error fetching PullRequest",
|
name: "Error fetching PullRequest",
|
||||||
@@ -147,7 +148,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: true,
|
reviewed: true,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &baseConfig)
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &baseConfig)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -179,7 +180,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: false,
|
reviewed: false,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &baseConfig)
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &baseConfig)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -207,7 +208,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: false,
|
reviewed: false,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -241,7 +242,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: true,
|
reviewed: true,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -275,7 +276,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: true,
|
reviewed: true,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -311,7 +312,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: false,
|
reviewed: false,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -346,7 +347,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: true,
|
reviewed: true,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -388,7 +389,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: true,
|
reviewed: true,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -430,7 +431,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: false,
|
reviewed: false,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -473,7 +474,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: false,
|
reviewed: false,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
return common.FetchPRSet("test", mock, "foo", "barPrj", 42, &common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -500,7 +501,7 @@ func TestPR(t *testing.T) {
|
|||||||
prjGitPRIndex: 0,
|
prjGitPRIndex: 0,
|
||||||
consistentSet: true,
|
consistentSet: true,
|
||||||
reviewed: true,
|
reviewed: true,
|
||||||
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineReviewFetcher) (*common.PRSet, error) {
|
reviewSetFetcher: func(mock *mock_common.MockGiteaPRTimelineFetcher) (*common.PRSet, error) {
|
||||||
config := common.AutogitConfig{
|
config := common.AutogitConfig{
|
||||||
Reviewers: []string{"+super1", "*super2", "m1", "-m2", "~*bot"},
|
Reviewers: []string{"+super1", "*super2", "m1", "-m2", "~*bot"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
@@ -515,7 +516,7 @@ func TestPR(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
pr_mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
pr_mock := mock_common.NewMockGiteaPRTimelineFetcher(ctl)
|
||||||
review_mock := mock_common.NewMockGiteaPRChecker(ctl)
|
review_mock := mock_common.NewMockGiteaPRChecker(ctl)
|
||||||
// reviewer_mock := mock_common.NewMockGiteaReviewRequester(ctl)
|
// reviewer_mock := mock_common.NewMockGiteaReviewRequester(ctl)
|
||||||
|
|
||||||
@@ -619,508 +620,283 @@ func TestPR(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindMissingAndExtraReviewers(t *testing.T) {
|
func TestPRAssignReviewers(t *testing.T) {
|
||||||
|
t.Skip("FAIL: unexpected calls, missing calls")
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
config common.AutogitConfig
|
||||||
reviewers []struct {
|
reviewers []struct {
|
||||||
org, repo string
|
org, repo string
|
||||||
num int64
|
num int64
|
||||||
reviewer string
|
reviewer string
|
||||||
}
|
}
|
||||||
|
|
||||||
prset *common.PRSet
|
pkgReviews []*models.PullReview
|
||||||
maintainers common.MaintainershipData
|
pkgTimeline []*models.TimelineComment
|
||||||
|
prjReviews []*models.PullReview
|
||||||
|
prjTimeline []*models.TimelineComment
|
||||||
|
|
||||||
noAutoStaging bool
|
expectedReviewerCall [2][]string
|
||||||
|
|
||||||
expected_missing_reviewers [][]string
|
|
||||||
expected_extra_reviewers [][]string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "No reviewers",
|
name: "No reviewers",
|
||||||
prset: &common.PRSet{
|
config: common.AutogitConfig{
|
||||||
PRs: []*common.PRInfo{
|
GitProjectName: "prg/repo#main",
|
||||||
{
|
Organization: "org",
|
||||||
PR: &models.PullRequest{
|
Branch: "main",
|
||||||
User: &models.User{UserName: "foo"},
|
Reviewers: []string{},
|
||||||
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{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{}},
|
expectedReviewerCall: [2][]string{{"autogits_obs_staging_bot"}, {"prjmaintainer", "pkgmaintainer"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "One project reviewer only",
|
name: "One project reviewer only",
|
||||||
prset: &common.PRSet{
|
config: common.AutogitConfig{
|
||||||
PRs: []*common.PRInfo{
|
GitProjectName: "prg/repo#main",
|
||||||
{
|
Organization: "org",
|
||||||
PR: &models.PullRequest{
|
Branch: "main",
|
||||||
User: &models.User{UserName: "foo"},
|
Reviewers: []string{"-user1"},
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "foo"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Config: &common.AutogitConfig{
|
|
||||||
GitProjectName: "prg/repo#main",
|
|
||||||
Organization: "org",
|
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{}},
|
|
||||||
|
|
||||||
expected_missing_reviewers: [][]string{
|
|
||||||
[]string{},
|
|
||||||
[]string{"autogits_obs_staging_bot", "user1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "One project reviewer only and no auto staging",
|
|
||||||
noAutoStaging: true,
|
|
||||||
prset: &common.PRSet{
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "foo"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "foo"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Config: &common.AutogitConfig{
|
|
||||||
GitProjectName: "prg/repo#main",
|
|
||||||
Organization: "org",
|
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{}},
|
|
||||||
|
|
||||||
expected_missing_reviewers: [][]string{
|
|
||||||
nil,
|
|
||||||
{"user1"},
|
|
||||||
},
|
},
|
||||||
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}, {"prjmaintainer", "pkgmaintainer"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "One project reviewer and one pkg reviewer only",
|
name: "One project reviewer and one pkg reviewer only",
|
||||||
prset: &common.PRSet{
|
config: common.AutogitConfig{
|
||||||
PRs: []*common.PRInfo{
|
GitProjectName: "prg/repo#main",
|
||||||
{
|
Organization: "org",
|
||||||
PR: &models.PullRequest{
|
Branch: "main",
|
||||||
User: &models.User{UserName: "foo"},
|
Reviewers: []string{"-user1", "user2"},
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "foo"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Config: &common.AutogitConfig{
|
|
||||||
GitProjectName: "prg/repo#main",
|
|
||||||
Organization: "org",
|
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1", "user2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{}},
|
|
||||||
|
|
||||||
expected_missing_reviewers: [][]string{
|
|
||||||
[]string{"user2"},
|
|
||||||
[]string{"autogits_obs_staging_bot", "user1"},
|
|
||||||
},
|
},
|
||||||
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}, {"user2", "prjmaintainer", "pkgmaintainer"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "No need to get reviews of submitter reviewer",
|
name: "No need to get reviews of submitter",
|
||||||
prset: &common.PRSet{
|
config: common.AutogitConfig{
|
||||||
PRs: []*common.PRInfo{
|
GitProjectName: "prg/repo#main",
|
||||||
{
|
Organization: "org",
|
||||||
PR: &models.PullRequest{
|
Branch: "main",
|
||||||
User: &models.User{UserName: "submitter"},
|
Reviewers: []string{"-user1", "submitter"},
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{{State: common.ReviewStateRequestReview, User: &models.User{UserName: "m1"}}},
|
|
||||||
RequestedReviewers: []string{"m1"},
|
|
||||||
FullTimeline: []*models.TimelineComment{
|
|
||||||
{User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "m1"}, Type: common.TimelineCommentType_ReviewRequested},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "foo"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Config: &common.AutogitConfig{
|
|
||||||
GitProjectName: "prg/repo#main",
|
|
||||||
Organization: "org",
|
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1", "submitter"},
|
|
||||||
},
|
|
||||||
BotUser: "bot",
|
|
||||||
},
|
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"m1", "submitter"}}},
|
|
||||||
|
|
||||||
expected_missing_reviewers: [][]string{
|
|
||||||
nil,
|
|
||||||
{"autogits_obs_staging_bot", "user1"},
|
|
||||||
},
|
|
||||||
expected_extra_reviewers: [][]string{
|
|
||||||
{"m1"},
|
|
||||||
},
|
},
|
||||||
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}, {"prjmaintainer", "pkgmaintainer"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "No need to get reviews of submitter maintainer",
|
name: "Reviews are done",
|
||||||
prset: &common.PRSet{
|
config: common.AutogitConfig{
|
||||||
PRs: []*common.PRInfo{
|
GitProjectName: "prg/repo#main",
|
||||||
{
|
Organization: "org",
|
||||||
PR: &models.PullRequest{
|
Branch: "main",
|
||||||
User: &models.User{UserName: "submitter"},
|
Reviewers: []string{"-user1", "user2"},
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
},
|
||||||
},
|
pkgReviews: []*models.PullReview{
|
||||||
Reviews: &common.PRReviews{},
|
{
|
||||||
},
|
State: common.ReviewStateApproved,
|
||||||
{
|
User: &models.User{UserName: "user2"},
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "foo"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Config: &common.AutogitConfig{
|
{
|
||||||
GitProjectName: "prg/repo#main",
|
State: common.ReviewStateApproved,
|
||||||
Organization: "org",
|
User: &models.User{UserName: "pkgmaintainer"},
|
||||||
Branch: "main",
|
},
|
||||||
Reviewers: []string{"-user1", "submitter"},
|
{
|
||||||
|
State: common.ReviewStatePending,
|
||||||
|
User: &models.User{UserName: "prjmaintainer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"submitter"}}},
|
prjReviews: []*models.PullReview{
|
||||||
|
{
|
||||||
expected_missing_reviewers: [][]string{
|
State: common.ReviewStateRequestChanges,
|
||||||
[]string{},
|
User: &models.User{UserName: "user1"},
|
||||||
[]string{"autogits_obs_staging_bot", "user1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Add reviewer if also maintainer where review by maintainer is not needed",
|
|
||||||
prset: &common.PRSet{
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "submitter"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Config: &common.AutogitConfig{
|
{
|
||||||
GitProjectName: "prg/repo#main",
|
State: common.ReviewStateRequestReview,
|
||||||
Organization: "org",
|
User: &models.User{UserName: "autogits_obs_staging_bot"},
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1", "submitter", "*reviewer"},
|
|
||||||
},
|
},
|
||||||
BotUser: "bot",
|
|
||||||
},
|
},
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"submitter", "reviewer"}, "": []string{"reviewer"}}},
|
expectedReviewerCall: [2][]string{},
|
||||||
|
|
||||||
expected_missing_reviewers: [][]string{
|
|
||||||
[]string{"reviewer"},
|
|
||||||
[]string{"autogits_obs_staging_bot", "reviewer", "user1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Dont remove reviewer if also maintainer",
|
|
||||||
prset: &common.PRSet{
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "submitter"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{{State: common.ReviewStateRequestReview, User: &models.User{UserName: "reviewer"}}},
|
|
||||||
RequestedReviewers: []string{"reviewer"},
|
|
||||||
FullTimeline: []*models.TimelineComment{{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "reviewer"}}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{{State: common.ReviewStateRequestReview, User: &models.User{UserName: "reviewer"}}},
|
|
||||||
RequestedReviewers: []string{"reviewer"},
|
|
||||||
FullTimeline: []*models.TimelineComment{{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "reviewer"}}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Config: &common.AutogitConfig{
|
|
||||||
GitProjectName: "prg/repo#main",
|
|
||||||
Organization: "org",
|
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1", "submitter", "*reviewer"},
|
|
||||||
},
|
|
||||||
BotUser: "bot",
|
|
||||||
},
|
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"submitter", "reviewer"}, "": []string{"reviewer"}}},
|
|
||||||
|
|
||||||
expected_missing_reviewers: [][]string{
|
|
||||||
[]string{},
|
|
||||||
[]string{"autogits_obs_staging_bot", "user1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Extra project reviewer on the package",
|
|
||||||
prset: &common.PRSet{
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "submitter"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{
|
|
||||||
{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}},
|
|
||||||
{State: common.ReviewStateApproved, User: &models.User{UserName: "pkgmaintainer"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "prjmaintainer"}},
|
|
||||||
},
|
|
||||||
RequestedReviewers: []string{"user2", "pkgmaintainer", "prjmaintainer"},
|
|
||||||
FullTimeline: []*models.TimelineComment{
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "user2"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgmaintainer"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prjmaintainer"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{
|
|
||||||
{State: common.ReviewStateRequestChanges, User: &models.User{UserName: "user1"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "autogits_obs_staging_bot"}},
|
|
||||||
},
|
|
||||||
RequestedReviewers: []string{"user1", "autogits_obs_staging_bot"},
|
|
||||||
FullTimeline: []*models.TimelineComment{
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "user1"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "autogits_obs_staging_bot"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Config: &common.AutogitConfig{
|
|
||||||
GitProjectName: "prg/repo#main",
|
|
||||||
Organization: "org",
|
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1", "submitter"},
|
|
||||||
},
|
|
||||||
BotUser: "bot",
|
|
||||||
},
|
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"pkgmaintainer"}, "": {"prjmaintainer"}}},
|
|
||||||
|
|
||||||
expected_missing_reviewers: [][]string{},
|
|
||||||
expected_extra_reviewers: [][]string{{"prjmaintainer"}},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Extra project reviewers on the package and project",
|
name: "Stale review is not done, re-request it",
|
||||||
prset: &common.PRSet{
|
config: common.AutogitConfig{
|
||||||
PRs: []*common.PRInfo{
|
GitProjectName: "org/repo#main",
|
||||||
{
|
Organization: "org",
|
||||||
PR: &models.PullRequest{
|
Branch: "main",
|
||||||
User: &models.User{UserName: "submitter"},
|
Reviewers: []string{"-user1", "user2"},
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{
|
|
||||||
{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}},
|
|
||||||
{State: common.ReviewStateApproved, User: &models.User{UserName: "pkgmaintainer"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "prjmaintainer"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "pkgm1"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "pkgm2"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "prj1"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "prj2"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "someother"}},
|
|
||||||
},
|
|
||||||
RequestedReviewers: []string{"user2", "pkgmaintainer", "prjmaintainer", "pkgm1", "pkgm2", "someother", "prj1", "prj2"},
|
|
||||||
FullTimeline: []*models.TimelineComment{
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgmaintainer"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prjmaintainer"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj1"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj2"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgm1"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgm2"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prg"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{
|
|
||||||
{State: common.ReviewStateRequestChanges, User: &models.User{UserName: "user1"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "autogits_obs_staging_bot"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "prj1"}},
|
|
||||||
{State: common.ReviewStatePending, User: &models.User{UserName: "prj2"}},
|
|
||||||
},
|
|
||||||
RequestedReviewers: []string{"user1", "autogits_obs_staging_bot", "prj1", "prj2"},
|
|
||||||
FullTimeline: []*models.TimelineComment{
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "autogits_obs_staging_bot"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj1"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj2"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Config: &common.AutogitConfig{
|
|
||||||
GitProjectName: "prg/repo#main",
|
|
||||||
Organization: "org",
|
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1", "submitter"},
|
|
||||||
},
|
|
||||||
BotUser: "bot",
|
|
||||||
},
|
},
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"pkgmaintainer", "pkgm1", "pkgm2"}, "": {"prjmaintainer", "prj1", "prj2"}}},
|
pkgReviews: []*models.PullReview{
|
||||||
|
{
|
||||||
expected_missing_reviewers: [][]string{},
|
State: common.ReviewStateApproved,
|
||||||
expected_extra_reviewers: [][]string{{"pkgm1", "pkgm2", "prj1", "prj2", "prjmaintainer"}, {"prj1", "prj2"}},
|
User: &models.User{UserName: "user2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
State: common.ReviewStatePending,
|
||||||
|
User: &models.User{UserName: "prjmaintainer"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prjReviews: []*models.PullReview{
|
||||||
|
{
|
||||||
|
State: common.ReviewStateRequestChanges,
|
||||||
|
User: &models.User{UserName: "user1"},
|
||||||
|
Stale: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
State: common.ReviewStateRequestReview,
|
||||||
|
Stale: true,
|
||||||
|
User: &models.User{UserName: "autogits_obs_staging_bot"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}, {"pkgmaintainer"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "No extra project reviewers on the package and project (all pending)",
|
name: "Stale optional review is not done, re-request it",
|
||||||
prset: &common.PRSet{
|
config: common.AutogitConfig{
|
||||||
PRs: []*common.PRInfo{
|
GitProjectName: "prg/repo#main",
|
||||||
{
|
Organization: "org",
|
||||||
PR: &models.PullRequest{
|
Branch: "main",
|
||||||
User: &models.User{UserName: "submitter"},
|
Reviewers: []string{"-user1", "user2", "~bot"},
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg", Owner: &models.User{UserName: "org"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{
|
|
||||||
{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "pkgmaintainer"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "prjmaintainer"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "pkgm1"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "prj1"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "someother"}},
|
|
||||||
},
|
|
||||||
RequestedReviewers: []string{"user2", "pkgmaintainer", "prjmaintainer", "pkgm1", "someother", "prj1"},
|
|
||||||
FullTimeline: []*models.TimelineComment{
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgm1"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "pkgmaintainer"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prjmaintainer"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj1"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "!bot"}, Assignee: &models.User{UserName: "someother"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "prj"}}},
|
|
||||||
},
|
|
||||||
Reviews: &common.PRReviews{
|
|
||||||
Reviews: []*models.PullReview{
|
|
||||||
{State: common.ReviewStateRequestChanges, User: &models.User{UserName: "user1"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "autogits_obs_staging_bot"}},
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "prj1"}},
|
|
||||||
},
|
|
||||||
RequestedReviewers: []string{"user1", "autogits_obs_staging_bot", "prj1"},
|
|
||||||
FullTimeline: []*models.TimelineComment{
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "autogits_obs_staging_bot"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "bot"}, Assignee: &models.User{UserName: "prj1"}},
|
|
||||||
{Type: common.TimelineCommentType_ReviewRequested, User: &models.User{UserName: "!bot"}, Assignee: &models.User{UserName: "user1"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Config: &common.AutogitConfig{
|
|
||||||
GitProjectName: "prj/repo#main",
|
|
||||||
Organization: "org",
|
|
||||||
Branch: "main",
|
|
||||||
Reviewers: []string{"-user1", "submitter"},
|
|
||||||
},
|
|
||||||
BotUser: "bot",
|
|
||||||
},
|
},
|
||||||
maintainers: &common.MaintainershipMap{Data: map[string][]string{"pkg": []string{"pkgmaintainer", "pkgm1", "pkgm2"}, "": {"prjmaintainer", "prj1", "prj2"}}},
|
pkgReviews: []*models.PullReview{
|
||||||
|
{
|
||||||
expected_missing_reviewers: [][]string{{"pkgm2", "prj2"}},
|
State: common.ReviewStateApproved,
|
||||||
expected_extra_reviewers: [][]string{{}, {"prj1"}},
|
User: &models.User{UserName: "bot"},
|
||||||
|
Stale: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
State: common.ReviewStateApproved,
|
||||||
|
User: &models.User{UserName: "user2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
State: common.ReviewStatePending,
|
||||||
|
User: &models.User{UserName: "prjmaintainer"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prjReviews: []*models.PullReview{
|
||||||
|
{
|
||||||
|
State: common.ReviewStateRequestChanges,
|
||||||
|
User: &models.User{UserName: "user1"},
|
||||||
|
Stale: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
State: common.ReviewStateRequestReview,
|
||||||
|
Stale: true,
|
||||||
|
User: &models.User{UserName: "autogits_obs_staging_bot"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedReviewerCall: [2][]string{{"user1", "autogits_obs_staging_bot"}, {"pkgmaintainer", "bot"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
test.prset.HasAutoStaging = !test.noAutoStaging
|
ctl := gomock.NewController(t)
|
||||||
for idx, pr := range test.prset.PRs {
|
pr_mock := mock_common.NewMockGiteaPRTimelineFetcher(ctl)
|
||||||
missing, extra := test.prset.FindMissingAndExtraReviewers(test.maintainers, idx)
|
review_mock := mock_common.NewMockGiteaReviewFetcherAndRequester(ctl)
|
||||||
|
maintainership_mock := mock_common.NewMockMaintainershipData(ctl)
|
||||||
|
|
||||||
// avoid nil dereference below, by adding empty array elements
|
if test.pkgTimeline == nil {
|
||||||
if idx >= len(test.expected_missing_reviewers) {
|
test.pkgTimeline = reviewsToTimeline(test.pkgReviews)
|
||||||
test.expected_missing_reviewers = append(test.expected_missing_reviewers, nil)
|
}
|
||||||
}
|
if test.prjTimeline == nil {
|
||||||
if idx >= len(test.expected_extra_reviewers) {
|
test.prjTimeline = reviewsToTimeline(test.prjReviews)
|
||||||
test.expected_extra_reviewers = append(test.expected_extra_reviewers, nil)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
slices.Sort(test.expected_extra_reviewers[idx])
|
pr_mock.EXPECT().GetPullRequest("other", "pkgrepo", int64(1)).Return(&models.PullRequest{
|
||||||
slices.Sort(test.expected_missing_reviewers[idx])
|
Body: "Some description is here",
|
||||||
if slices.Compare(missing, test.expected_missing_reviewers[idx]) != 0 {
|
User: &models.User{UserName: "submitter"},
|
||||||
t.Error("Expected missing reviewers for", common.PRtoString(pr.PR), ":", test.expected_missing_reviewers[idx], "but have:", missing)
|
RequestedReviewers: []*models.User{},
|
||||||
}
|
Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "pkgrepo", Owner: &models.User{UserName: "other"}}},
|
||||||
|
Head: &models.PRBranchInfo{},
|
||||||
|
Index: 1,
|
||||||
|
}, nil)
|
||||||
|
review_mock.EXPECT().GetPullRequestReviews("other", "pkgrepo", int64(1)).Return(test.pkgReviews, nil)
|
||||||
|
review_mock.EXPECT().GetTimeline("other", "pkgrepo", int64(1)).Return(test.pkgTimeline, nil)
|
||||||
|
pr_mock.EXPECT().GetPullRequest("org", "repo", int64(1)).Return(&models.PullRequest{
|
||||||
|
Body: fmt.Sprintf(common.PrPattern, "other", "pkgrepo", 1),
|
||||||
|
User: &models.User{UserName: "bot1"},
|
||||||
|
RequestedReviewers: []*models.User{{UserName: "main_reviewer"}},
|
||||||
|
Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "org"}}},
|
||||||
|
Head: &models.PRBranchInfo{},
|
||||||
|
Index: 42,
|
||||||
|
}, nil)
|
||||||
|
review_mock.EXPECT().GetPullRequestReviews("org", "repo", int64(42)).Return(test.prjReviews, nil)
|
||||||
|
review_mock.EXPECT().GetTimeline("org", "repo", int64(42)).Return(test.prjTimeline, nil)
|
||||||
|
|
||||||
if slices.Compare(extra, test.expected_extra_reviewers[idx]) != 0 {
|
maintainership_mock.EXPECT().ListProjectMaintainers(gomock.Any()).Return([]string{"prjmaintainer"}).AnyTimes()
|
||||||
t.Error("Expected reviewers to remove for", common.PRtoString(pr.PR), ":", test.expected_extra_reviewers[idx], "but have:", extra)
|
maintainership_mock.EXPECT().ListPackageMaintainers("pkgrepo", gomock.Any()).Return([]string{"pkgmaintainer"}).AnyTimes()
|
||||||
|
|
||||||
|
prs, _ := common.FetchPRSet("test", pr_mock, "other", "pkgrepo", int64(1), &test.config)
|
||||||
|
if len(prs.PRs) != 2 {
|
||||||
|
t.Fatal("PRs not fetched")
|
||||||
|
}
|
||||||
|
for _, pr := range prs.PRs {
|
||||||
|
r := test.expectedReviewerCall[0]
|
||||||
|
if !prs.IsPrjGitPR(pr.PR) {
|
||||||
|
r = test.expectedReviewerCall[1]
|
||||||
|
}
|
||||||
|
slices.Sort(r)
|
||||||
|
for _, reviewer := range r {
|
||||||
|
review_mock.EXPECT().RequestReviews(pr.PR, reviewer).Return(nil, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
prs.AssignReviewers(review_mock, maintainership_mock)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
prjgit_tests := []struct {
|
||||||
|
name string
|
||||||
|
config common.AutogitConfig
|
||||||
|
reviewers []struct {
|
||||||
|
org, repo string
|
||||||
|
num int64
|
||||||
|
reviewer string
|
||||||
|
}
|
||||||
|
|
||||||
|
prjReviews []*models.PullReview
|
||||||
|
|
||||||
|
expectedReviewerCall [2][]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "PrjMaintainers in prjgit review when not part of pkg set",
|
||||||
|
config: common.AutogitConfig{
|
||||||
|
GitProjectName: "org/repo#main",
|
||||||
|
Organization: "org",
|
||||||
|
Branch: "main",
|
||||||
|
Reviewers: []string{},
|
||||||
|
},
|
||||||
|
expectedReviewerCall: [2][]string{{"autogits_obs_staging_bot", "prjmaintainer"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range prjgit_tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
ctl := gomock.NewController(t)
|
||||||
|
pr_mock := mock_common.NewMockGiteaPRTimelineFetcher(ctl)
|
||||||
|
review_mock := mock_common.NewMockGiteaReviewFetcherAndRequester(ctl)
|
||||||
|
maintainership_mock := mock_common.NewMockMaintainershipData(ctl)
|
||||||
|
|
||||||
|
pr_mock.EXPECT().GetPullRequest("org", "repo", int64(1)).Return(&models.PullRequest{
|
||||||
|
Body: "Some description is here",
|
||||||
|
User: &models.User{UserName: "submitter"},
|
||||||
|
RequestedReviewers: []*models.User{},
|
||||||
|
Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "repo", Owner: &models.User{UserName: "org"}}},
|
||||||
|
Head: &models.PRBranchInfo{},
|
||||||
|
Index: 1,
|
||||||
|
}, nil)
|
||||||
|
review_mock.EXPECT().GetPullRequestReviews("org", "repo", int64(1)).Return(test.prjReviews, nil)
|
||||||
|
review_mock.EXPECT().GetTimeline("org", "repo", int64(1)).Return(nil, nil)
|
||||||
|
|
||||||
|
maintainership_mock.EXPECT().ListProjectMaintainers(gomock.Any()).Return([]string{"prjmaintainer"}).AnyTimes()
|
||||||
|
|
||||||
|
prs, _ := common.FetchPRSet("test", pr_mock, "org", "repo", int64(1), &test.config)
|
||||||
|
if len(prs.PRs) != 1 {
|
||||||
|
t.Fatal("PRs not fetched")
|
||||||
|
}
|
||||||
|
for _, pr := range prs.PRs {
|
||||||
|
r := test.expectedReviewerCall[0]
|
||||||
|
if !prs.IsPrjGitPR(pr.PR) {
|
||||||
|
t.Fatal("only prjgit pr here")
|
||||||
|
}
|
||||||
|
for _, reviewer := range r {
|
||||||
|
review_mock.EXPECT().RequestReviews(pr.PR, reviewer).Return(nil, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prs.AssignReviewers(review_mock, maintainership_mock)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1165,6 +941,7 @@ func TestPRMerge(t *testing.T) {
|
|||||||
pr: &models.PullRequest{
|
pr: &models.PullRequest{
|
||||||
Base: &models.PRBranchInfo{
|
Base: &models.PRBranchInfo{
|
||||||
Sha: "e8b0de43d757c96a9d2c7101f4bff404e322f53a1fa4041fb85d646110c38ad4", // "base_add_b1"
|
Sha: "e8b0de43d757c96a9d2c7101f4bff404e322f53a1fa4041fb85d646110c38ad4", // "base_add_b1"
|
||||||
|
Name: "master",
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
Name: "prj",
|
Name: "prj",
|
||||||
Owner: &models.User{
|
Owner: &models.User{
|
||||||
@@ -1185,6 +962,7 @@ func TestPRMerge(t *testing.T) {
|
|||||||
pr: &models.PullRequest{
|
pr: &models.PullRequest{
|
||||||
Base: &models.PRBranchInfo{
|
Base: &models.PRBranchInfo{
|
||||||
Sha: "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8",
|
Sha: "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8",
|
||||||
|
Name: "master",
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
Name: "prj",
|
Name: "prj",
|
||||||
Owner: &models.User{
|
Owner: &models.User{
|
||||||
@@ -1203,7 +981,8 @@ func TestPRMerge(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
mock := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
mock := mock_common.NewMockGiteaPRTimelineFetcher(ctl)
|
||||||
|
|
||||||
reviewUnrequestMock := mock_common.NewMockGiteaReviewUnrequester(ctl)
|
reviewUnrequestMock := mock_common.NewMockGiteaReviewUnrequester(ctl)
|
||||||
|
|
||||||
reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
reviewUnrequestMock.EXPECT().UnrequestReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||||||
@@ -1262,7 +1041,7 @@ func TestPRChanges(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
mock_fetcher := mock_common.NewMockGiteaPRTimelineReviewFetcher(ctl)
|
mock_fetcher := mock_common.NewMockGiteaPRTimelineFetcher(ctl)
|
||||||
mock_fetcher.EXPECT().GetPullRequest("org", "prjgit", int64(42)).Return(test.PrjPRs, nil)
|
mock_fetcher.EXPECT().GetPullRequest("org", "prjgit", int64(42)).Return(test.PrjPRs, nil)
|
||||||
for _, pr := range test.PRs {
|
for _, pr := range test.PRs {
|
||||||
mock_fetcher.EXPECT().GetPullRequest(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(pr, nil)
|
mock_fetcher.EXPECT().GetPullRequest(pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index).Return(pr, nil)
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
type Reviewers struct {
|
type Reviewers struct {
|
||||||
Prj []string
|
Prj []string
|
||||||
Pkg []string
|
Pkg []string
|
||||||
@@ -32,5 +36,10 @@ func ParseReviewers(input []string) *Reviewers {
|
|||||||
*pkg = append(*pkg, reviewer)
|
*pkg = append(*pkg, reviewer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(r.Prj, Bot_BuildReview) {
|
||||||
|
r.Prj = append(r.Prj, Bot_BuildReview)
|
||||||
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ func TestReviewers(t *testing.T) {
|
|||||||
name: "project and package reviewers",
|
name: "project and package reviewers",
|
||||||
input: []string{"1", "2", "3", "*5", "+6", "-7"},
|
input: []string{"1", "2", "3", "*5", "+6", "-7"},
|
||||||
|
|
||||||
prj: []string{"5", "7"},
|
prj: []string{"5", "7", common.Bot_BuildReview},
|
||||||
pkg: []string{"1", "2", "3", "5", "6"},
|
pkg: []string{"1", "2", "3", "5", "6"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "optional project and package reviewers",
|
name: "optional project and package reviewers",
|
||||||
input: []string{"~1", "2", "3", "~*5", "+6", "-7"},
|
input: []string{"~1", "2", "3", "~*5", "+6", "-7"},
|
||||||
|
|
||||||
prj: []string{"7"},
|
prj: []string{"7", common.Bot_BuildReview},
|
||||||
pkg: []string{"2", "3", "6"},
|
pkg: []string{"2", "3", "6"},
|
||||||
prj_optional: []string{"5"},
|
prj_optional: []string{"5"},
|
||||||
pkg_optional: []string{"1", "5"},
|
pkg_optional: []string{"1", "5"},
|
||||||
|
|||||||
@@ -9,14 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PRReviews struct {
|
type PRReviews struct {
|
||||||
Reviews []*models.PullReview
|
reviews []*models.PullReview
|
||||||
RequestedReviewers []string
|
reviewers []string
|
||||||
Comments []*models.TimelineComment
|
comments []*models.TimelineComment
|
||||||
|
|
||||||
FullTimeline []*models.TimelineComment
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func FetchGiteaReviews(rf GiteaReviewTimelineFetcher, org, repo string, no int64) (*PRReviews, error) {
|
func FetchGiteaReviews(rf GiteaReviewTimelineFetcher, reviewers []string, org, repo string, no int64) (*PRReviews, error) {
|
||||||
timeline, err := rf.GetTimeline(org, repo, no)
|
timeline, err := rf.GetTimeline(org, repo, no)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -27,14 +25,10 @@ func FetchGiteaReviews(rf GiteaReviewTimelineFetcher, org, repo string, no int64
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
reviews := make([]*models.PullReview, 0, 10)
|
reviews := make([]*models.PullReview, 0, len(reviewers))
|
||||||
needNewReviews := []string{}
|
|
||||||
var comments []*models.TimelineComment
|
var comments []*models.TimelineComment
|
||||||
|
|
||||||
alreadyHaveUserReview := func(user string) bool {
|
alreadyHaveUserReview := func(user string) bool {
|
||||||
if slices.Contains(needNewReviews, user) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, r := range reviews {
|
for _, r := range reviews {
|
||||||
if r.User != nil && r.User.UserName == user {
|
if r.User != nil && r.User.UserName == user {
|
||||||
return true
|
return true
|
||||||
@@ -43,40 +37,32 @@ func FetchGiteaReviews(rf GiteaReviewTimelineFetcher, org, repo string, no int64
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebug("FetchingGiteaReviews for", org, repo, no)
|
|
||||||
LogDebug("Number of reviews:", len(rawReviews))
|
|
||||||
LogDebug("Number of items in timeline:", len(timeline))
|
|
||||||
|
|
||||||
cutOffIdx := len(timeline)
|
|
||||||
for idx, item := range timeline {
|
for idx, item := range timeline {
|
||||||
if item.Type == TimelineCommentType_Review || item.Type == TimelineCommentType_ReviewRequested {
|
if item.Type == TimelineCommentType_Review {
|
||||||
for _, r := range rawReviews {
|
for _, r := range rawReviews {
|
||||||
if r.ID == item.ReviewID {
|
if r.ID == item.ReviewID {
|
||||||
if !alreadyHaveUserReview(r.User.UserName) {
|
if !alreadyHaveUserReview(r.User.UserName) {
|
||||||
if item.Type == TimelineCommentType_Review && idx > cutOffIdx {
|
reviews = append(reviews, r)
|
||||||
needNewReviews = append(needNewReviews, r.User.UserName)
|
|
||||||
} else {
|
|
||||||
reviews = append(reviews, r)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if item.Type == TimelineCommentType_Comment && cutOffIdx > idx {
|
} else if item.Type == TimelineCommentType_Comment {
|
||||||
comments = append(comments, item)
|
comments = append(comments, item)
|
||||||
} else if item.Type == TimelineCommentType_PushPull && cutOffIdx == len(timeline) {
|
} else if item.Type == TimelineCommentType_PushPull {
|
||||||
LogDebug("cut-off", item.Created, "@", idx)
|
LogDebug("cut-off", item.Created)
|
||||||
cutOffIdx = idx
|
timeline = timeline[0:idx]
|
||||||
|
break
|
||||||
} else {
|
} else {
|
||||||
LogDebug("Unhandled timeline type:", item.Type)
|
LogDebug("Unhandled timeline type:", item.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LogDebug("num comments:", len(comments), "timeline:", len(reviews))
|
LogDebug("num comments:", len(comments), "reviews:", len(reviews), len(timeline))
|
||||||
|
|
||||||
return &PRReviews{
|
return &PRReviews{
|
||||||
Reviews: reviews,
|
reviews: reviews,
|
||||||
Comments: comments,
|
reviewers: reviewers,
|
||||||
FullTimeline: timeline,
|
comments: comments,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,27 +81,23 @@ func bodyCommandManualMergeOK(body string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *PRReviews) IsManualMergeOK() bool {
|
func (r *PRReviews) IsManualMergeOK() bool {
|
||||||
if r == nil {
|
for _, c := range r.comments {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range r.Comments {
|
|
||||||
if c.Updated != c.Created {
|
if c.Updated != c.Created {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
LogDebug("comment:", c.User.UserName, c.Body)
|
LogDebug("comment:", c.User.UserName, c.Body)
|
||||||
if slices.Contains(r.RequestedReviewers, c.User.UserName) {
|
if slices.Contains(r.reviewers, c.User.UserName) {
|
||||||
if bodyCommandManualMergeOK(c.Body) {
|
if bodyCommandManualMergeOK(c.Body) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range r.Reviews {
|
for _, c := range r.reviews {
|
||||||
if c.Updated != c.Submitted {
|
if c.Updated != c.Submitted {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if slices.Contains(r.RequestedReviewers, c.User.UserName) {
|
if slices.Contains(r.reviewers, c.User.UserName) {
|
||||||
if bodyCommandManualMergeOK(c.Body) {
|
if bodyCommandManualMergeOK(c.Body) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -126,14 +108,11 @@ func (r *PRReviews) IsManualMergeOK() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *PRReviews) IsApproved() bool {
|
func (r *PRReviews) IsApproved() bool {
|
||||||
if r == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
goodReview := true
|
goodReview := true
|
||||||
|
|
||||||
for _, reviewer := range r.RequestedReviewers {
|
for _, reviewer := range r.reviewers {
|
||||||
goodReview = false
|
goodReview = false
|
||||||
for _, review := range r.Reviews {
|
for _, review := range r.reviews {
|
||||||
if review.User.UserName == reviewer && review.State == ReviewStateApproved && !review.Stale && !review.Dismissed {
|
if review.User.UserName == reviewer && review.State == ReviewStateApproved && !review.Stale && !review.Dismissed {
|
||||||
LogDebug(" -- found review: ", review.User.UserName)
|
LogDebug(" -- found review: ", review.User.UserName)
|
||||||
goodReview = true
|
goodReview = true
|
||||||
@@ -151,11 +130,7 @@ func (r *PRReviews) IsApproved() bool {
|
|||||||
|
|
||||||
func (r *PRReviews) MissingReviews() []string {
|
func (r *PRReviews) MissingReviews() []string {
|
||||||
missing := []string{}
|
missing := []string{}
|
||||||
if r == nil {
|
for _, reviewer := range r.reviewers {
|
||||||
return missing
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, reviewer := range r.RequestedReviewers {
|
|
||||||
if !r.IsReviewedBy(reviewer) {
|
if !r.IsReviewedBy(reviewer) {
|
||||||
missing = append(missing, reviewer)
|
missing = append(missing, reviewer)
|
||||||
}
|
}
|
||||||
@@ -163,64 +138,45 @@ func (r *PRReviews) MissingReviews() []string {
|
|||||||
return missing
|
return missing
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PRReviews) FindReviewRequester(reviewer string) *models.TimelineComment {
|
|
||||||
if r == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range r.FullTimeline {
|
|
||||||
if r.Type == TimelineCommentType_ReviewRequested && r.Assignee.UserName == reviewer {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *PRReviews) HasPendingReviewBy(reviewer string) bool {
|
func (r *PRReviews) HasPendingReviewBy(reviewer string) bool {
|
||||||
if r == nil {
|
if !slices.Contains(r.reviewers, reviewer) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range r.Reviews {
|
isPending := false
|
||||||
if r.User.UserName == reviewer {
|
for _, r := range r.reviews {
|
||||||
|
if r.User.UserName == reviewer && !r.Stale {
|
||||||
switch r.State {
|
switch r.State {
|
||||||
case ReviewStateRequestReview, ReviewStatePending:
|
case ReviewStateApproved:
|
||||||
return true
|
fallthrough
|
||||||
default:
|
case ReviewStateRequestChanges:
|
||||||
return false
|
return false
|
||||||
|
case ReviewStateRequestReview:
|
||||||
|
fallthrough
|
||||||
|
case ReviewStatePending:
|
||||||
|
isPending = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return isPending
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PRReviews) IsReviewedBy(reviewer string) bool {
|
func (r *PRReviews) IsReviewedBy(reviewer string) bool {
|
||||||
if r == nil {
|
if !slices.Contains(r.reviewers, reviewer) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range r.Reviews {
|
for _, r := range r.reviews {
|
||||||
if r.User.UserName == reviewer && !r.Stale {
|
if r.User.UserName == reviewer && !r.Stale {
|
||||||
switch r.State {
|
switch r.State {
|
||||||
case ReviewStateApproved, ReviewStateRequestChanges:
|
case ReviewStateApproved:
|
||||||
|
return true
|
||||||
|
case ReviewStateRequestChanges:
|
||||||
return true
|
return true
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PRReviews) IsReviewedByOneOf(reviewers ...string) bool {
|
|
||||||
for _, reviewer := range reviewers {
|
|
||||||
if r.IsReviewedBy(reviewer) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -62,23 +62,11 @@ func TestReviews(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Two reviewer, one stale and pending",
|
name: "Two reviewer, one stale and pending",
|
||||||
reviews: []*models.PullReview{
|
reviews: []*models.PullReview{
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "user1"}, Stale: true},
|
&models.PullReview{State: common.ReviewStateRequestReview, User: &models.User{UserName: "user1"}, Stale: true},
|
||||||
},
|
},
|
||||||
reviewers: []string{"user1", "user2"},
|
reviewers: []string{"user1", "user2"},
|
||||||
isApproved: false,
|
isApproved: false,
|
||||||
isPendingByTest1: true,
|
isPendingByTest1: false,
|
||||||
isReviewedByTest1: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Two reviewer, one stale and pending, other done",
|
|
||||||
reviews: []*models.PullReview{
|
|
||||||
{State: common.ReviewStateRequestReview, User: &models.User{UserName: "user1"}},
|
|
||||||
{State: common.ReviewStateRequestChanges, User: &models.User{UserName: "user1"}},
|
|
||||||
{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}},
|
|
||||||
},
|
|
||||||
reviewers: []string{"user1", "user2"},
|
|
||||||
isApproved: false,
|
|
||||||
isPendingByTest1: true,
|
|
||||||
isReviewedByTest1: false,
|
isReviewedByTest1: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -151,7 +139,7 @@ func TestReviews(t *testing.T) {
|
|||||||
rf.EXPECT().GetTimeline("test", "pr", int64(1)).Return(test.timeline, nil)
|
rf.EXPECT().GetTimeline("test", "pr", int64(1)).Return(test.timeline, nil)
|
||||||
rf.EXPECT().GetPullRequestReviews("test", "pr", int64(1)).Return(test.reviews, test.fetchErr)
|
rf.EXPECT().GetPullRequestReviews("test", "pr", int64(1)).Return(test.reviews, test.fetchErr)
|
||||||
|
|
||||||
reviews, err := common.FetchGiteaReviews(rf, "test", "pr", 1)
|
reviews, err := common.FetchGiteaReviews(rf, test.reviewers, "test", "pr", 1)
|
||||||
|
|
||||||
if test.fetchErr != nil {
|
if test.fetchErr != nil {
|
||||||
if err != test.fetchErr {
|
if err != test.fetchErr {
|
||||||
@@ -159,7 +147,6 @@ func TestReviews(t *testing.T) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reviews.RequestedReviewers = test.reviewers
|
|
||||||
|
|
||||||
if r := reviews.IsApproved(); r != test.isApproved {
|
if r := reviews.IsApproved(); r != test.isApproved {
|
||||||
t.Fatal("Unexpected IsReviewed():", r, "vs. expected", test.isApproved)
|
t.Fatal("Unexpected IsReviewed():", r, "vs. expected", test.isApproved)
|
||||||
|
|||||||
@@ -40,10 +40,24 @@ create_prjgit_sample() {
|
|||||||
git submodule -q add ../pkgB2 pkgB2
|
git submodule -q add ../pkgB2 pkgB2
|
||||||
git commit -q -m "pkgB2 added"
|
git commit -q -m "pkgB2 added"
|
||||||
|
|
||||||
git checkout main
|
git checkout -b base_rm_c main
|
||||||
git clean -ffxd
|
git clean -ffxd
|
||||||
git submodule -q add -f ../pkgB1 pkgB1
|
git rm pkgC
|
||||||
git commit -q -m "main adding pkgB1"
|
git commit -q -m 'pkgC removed'
|
||||||
|
|
||||||
|
git checkout -b base_modify_c main
|
||||||
|
git submodule update --init pkgC
|
||||||
|
pushd pkgC
|
||||||
|
echo "mofieid" >> README.md
|
||||||
|
git commit -q -m "modified" README.md
|
||||||
|
popd
|
||||||
|
git commit pkgC -m "modifiedC"
|
||||||
|
git submodule deinit -f pkgC
|
||||||
|
|
||||||
|
# git checkout main
|
||||||
|
# git clean -ffxd
|
||||||
|
# git submodule -q add -f ../pkgB1 pkgB1
|
||||||
|
# git commit -q -m "main adding pkgB1"
|
||||||
|
|
||||||
popd
|
popd
|
||||||
}
|
}
|
||||||
|
|||||||
110
common/utils.go
110
common/utils.go
@@ -27,87 +27,10 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NewRepos struct {
|
|
||||||
Repos []struct {
|
|
||||||
Organization, Repository, Branch string
|
|
||||||
PackageName string
|
|
||||||
}
|
|
||||||
IsMaintainer bool
|
|
||||||
}
|
|
||||||
|
|
||||||
const maintainership_line = "MAINTAINER"
|
|
||||||
|
|
||||||
var true_lines []string = []string{"1", "TRUE", "YES", "OK", "T"}
|
|
||||||
|
|
||||||
func HasSpace(s string) bool {
|
|
||||||
return strings.IndexFunc(s, unicode.IsSpace) >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func FindNewReposInIssueBody(body string) *NewRepos {
|
|
||||||
Issues := &NewRepos{}
|
|
||||||
for _, line := range strings.Split(body, "\n") {
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
if ul := strings.ToUpper(line); strings.HasPrefix(ul, "MAINTAINER") {
|
|
||||||
value := ""
|
|
||||||
if idx := strings.IndexRune(ul, ':'); idx > 0 && len(ul) > idx+2 {
|
|
||||||
value = ul[idx+1:]
|
|
||||||
} else if idx := strings.IndexRune(ul, ' '); idx > 0 && len(ul) > idx+2 {
|
|
||||||
value = ul[idx+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if slices.Contains(true_lines, strings.TrimSpace(value)) {
|
|
||||||
Issues.IsMaintainer = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// line = strings.TrimSpace(line)
|
|
||||||
issue := struct{ Organization, Repository, Branch, PackageName string }{}
|
|
||||||
|
|
||||||
branch := strings.Split(line, "#")
|
|
||||||
repo := strings.Split(branch[0], "/")
|
|
||||||
|
|
||||||
if len(branch) == 2 {
|
|
||||||
issue.Branch = strings.TrimSpace(branch[1])
|
|
||||||
}
|
|
||||||
if len(repo) == 2 {
|
|
||||||
issue.Organization = strings.TrimSpace(repo[0])
|
|
||||||
issue.Repository = strings.TrimSpace(repo[1])
|
|
||||||
issue.PackageName = issue.Repository
|
|
||||||
|
|
||||||
if idx := strings.Index(strings.ToUpper(issue.Branch), " AS "); idx > 0 && len(issue.Branch) > idx+5 {
|
|
||||||
issue.PackageName = strings.TrimSpace(issue.Branch[idx+3:])
|
|
||||||
issue.Branch = strings.TrimSpace(issue.Branch[0:idx])
|
|
||||||
}
|
|
||||||
|
|
||||||
if HasSpace(issue.Organization) || HasSpace(issue.Repository) || HasSpace(issue.PackageName) || HasSpace(issue.Branch) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
Issues.Repos = append(Issues.Repos, issue)
|
|
||||||
//PackageNameIdx := strings.Index(strings.ToUpper(line), " AS ")
|
|
||||||
//words := strings.Split(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(Issues.Repos) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return Issues
|
|
||||||
}
|
|
||||||
|
|
||||||
func IssueToString(issue *models.Issue) string {
|
|
||||||
if issue == nil {
|
|
||||||
return "(nil)"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s/%s#%d", issue.Repository.Owner, issue.Repository.Name, issue.Index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SplitLines(str string) []string {
|
func SplitLines(str string) []string {
|
||||||
return SplitStringNoEmpty(str, "\n")
|
return SplitStringNoEmpty(str, "\n")
|
||||||
}
|
}
|
||||||
@@ -245,10 +168,9 @@ func FetchDevelProjects() (DevelProjects, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var DevelProjectNotFound = errors.New("Devel project not found")
|
var DevelProjectNotFound = errors.New("Devel project not found")
|
||||||
|
|
||||||
func (d DevelProjects) GetDevelProject(pkg string) (string, error) {
|
func (d DevelProjects) GetDevelProject(pkg string) (string, error) {
|
||||||
for _, item := range d {
|
for _, item := range d {
|
||||||
if item.Package == pkg {
|
if item.Package == pkg {
|
||||||
return item.Project, nil
|
return item.Project, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,33 +178,3 @@ func (d DevelProjects) GetDevelProject(pkg string) (string, error) {
|
|||||||
return "", DevelProjectNotFound
|
return "", DevelProjectNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
var removedBranchNameSuffixes []string = []string{
|
|
||||||
"-rm",
|
|
||||||
"-removed",
|
|
||||||
"-deleted",
|
|
||||||
}
|
|
||||||
|
|
||||||
func findRemovedBranchSuffix(branchName string) string {
|
|
||||||
branchName = strings.ToLower(branchName)
|
|
||||||
|
|
||||||
for _, suffix := range removedBranchNameSuffixes {
|
|
||||||
if len(suffix) < len(branchName) && strings.HasSuffix(branchName, suffix) {
|
|
||||||
return suffix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsRemovedBranch(branchName string) bool {
|
|
||||||
return len(findRemovedBranchSuffix(branchName)) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func TrimRemovedBranchSuffix(branchName string) string {
|
|
||||||
suffix := findRemovedBranchSuffix(branchName)
|
|
||||||
if len(suffix) > 0 {
|
|
||||||
return branchName[0 : len(branchName)-len(suffix)]
|
|
||||||
}
|
|
||||||
|
|
||||||
return branchName
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package common_test
|
package common_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
@@ -166,142 +165,3 @@ func TestRemoteName(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemovedBranchName(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
branchName string
|
|
||||||
isRemoved bool
|
|
||||||
regularName string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Empty branch",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Removed suffix only",
|
|
||||||
branchName: "-rm",
|
|
||||||
isRemoved: false,
|
|
||||||
regularName: "-rm",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Capital suffix",
|
|
||||||
branchName: "Foo-Rm",
|
|
||||||
isRemoved: true,
|
|
||||||
regularName: "Foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Other suffixes",
|
|
||||||
isRemoved: true,
|
|
||||||
branchName: "Goo-Rm-DeleteD",
|
|
||||||
regularName: "Goo-Rm",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Other suffixes",
|
|
||||||
isRemoved: true,
|
|
||||||
branchName: "main-REMOVED",
|
|
||||||
regularName: "main",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Not removed separator",
|
|
||||||
isRemoved: false,
|
|
||||||
branchName: "main;REMOVED",
|
|
||||||
regularName: "main;REMOVED",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
if r := common.IsRemovedBranch(test.branchName); r != test.isRemoved {
|
|
||||||
t.Error("Expecting isRemoved:", test.isRemoved, "but received", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tn := common.TrimRemovedBranchSuffix(test.branchName); tn != test.regularName {
|
|
||||||
t.Error("Expected stripped branch name to be:", test.regularName, "but have:", tn)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewPackageIssueParsing(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
issues *common.NewRepos
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Nothing",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Basic repo",
|
|
||||||
input: "org/repo#branch",
|
|
||||||
issues: &common.NewRepos{
|
|
||||||
Repos: []struct{ Organization, Repository, Branch, PackageName string }{
|
|
||||||
{Organization: "org", Repository: "repo", Branch: "branch", PackageName: "repo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Default branch and junk lines and approval for maintainership",
|
|
||||||
input: "\n\nsome comments\n\norg1/repo2\n\nmaintainership: yes",
|
|
||||||
issues: &common.NewRepos{
|
|
||||||
Repos: []struct{ Organization, Repository, Branch, PackageName string }{
|
|
||||||
{Organization: "org1", Repository: "repo2", Branch: "", PackageName: "repo2"},
|
|
||||||
},
|
|
||||||
IsMaintainer: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Default branch and junk lines and no maintainership",
|
|
||||||
input: "\n\nsome comments\n\norg1/repo2\n\nmaintainership: NEVER",
|
|
||||||
issues: &common.NewRepos{
|
|
||||||
Repos: []struct{ Organization, Repository, Branch, PackageName string }{
|
|
||||||
{Organization: "org1", Repository: "repo2", Branch: "", PackageName: "repo2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "3 repos with comments and maintainership",
|
|
||||||
input: "\n\nsome comments for org1/repo2 are here and more\n\norg1/repo2#master\n org2/repo3#master\n some/repo3#m\nMaintainer ok",
|
|
||||||
issues: &common.NewRepos{
|
|
||||||
Repos: []struct{ Organization, Repository, Branch, PackageName string }{
|
|
||||||
{Organization: "org1", Repository: "repo2", Branch: "master", PackageName: "repo2"},
|
|
||||||
{Organization: "org2", Repository: "repo3", Branch: "master", PackageName: "repo3"},
|
|
||||||
{Organization: "some", Repository: "repo3", Branch: "m", PackageName: "repo3"},
|
|
||||||
},
|
|
||||||
IsMaintainer: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Invalid repos with spaces",
|
|
||||||
input: "or g/repo#branch\norg/r epo#branch\norg/repo#br anch\norg/repo#branch As foo ++",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Valid repos with spaces",
|
|
||||||
input: " org / repo # branch",
|
|
||||||
issues: &common.NewRepos{
|
|
||||||
Repos: []struct{ Organization, Repository, Branch, PackageName string }{
|
|
||||||
{Organization: "org", Repository: "repo", Branch: "branch", PackageName: "repo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Package name is not repo name",
|
|
||||||
input: " org / repo # branch as repo++ \nmaintainer true",
|
|
||||||
issues: &common.NewRepos{
|
|
||||||
Repos: []struct{ Organization, Repository, Branch, PackageName string }{
|
|
||||||
{Organization: "org", Repository: "repo", Branch: "branch", PackageName: "repo++"},
|
|
||||||
},
|
|
||||||
IsMaintainer: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
issue := common.FindNewReposInIssueBody(test.input)
|
|
||||||
if !reflect.DeepEqual(test.issues, issue) {
|
|
||||||
t.Error("Expected", test.issues, "but have", issue)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -58,30 +58,6 @@ sub ListPackages {
|
|||||||
return @packages;
|
return @packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub FactoryMd5 {
|
|
||||||
my ($package) = @_;
|
|
||||||
my $out = "";
|
|
||||||
|
|
||||||
if (system("osc ls openSUSE:Factory $package | grep -q build.specials.obscpio") == 0) {
|
|
||||||
system("mkdir _extract") == 0 || die "_extract exists or can't make it. Aborting.";
|
|
||||||
chdir("_extract") || die;
|
|
||||||
system("osc cat openSUSE:Factory $package build.specials.obscpio | cpio -dium 2> /dev/null") == 0 || die;
|
|
||||||
system("rm .* 2> /dev/null");
|
|
||||||
open( my $fh, "find -type f -exec /usr/bin/basename {} \\; | xargs md5sum | awk '{print \$1 FS \$2}' | grep -v d41d8cd98f00b204e9800998ecf8427e |") or die;
|
|
||||||
while ( my $l = <$fh>) {
|
|
||||||
$out = $out.$l;
|
|
||||||
}
|
|
||||||
close($fh);
|
|
||||||
chdir("..") && system("rm -rf _extract") == 0 || die;
|
|
||||||
}
|
|
||||||
open( my $fh, "osc ls -v openSUSE:Factory $package | awk '{print \$1 FS \$7}' | grep -v -F '_scmsync.obsinfo\nbuild.specials.obscpio' |") or die;
|
|
||||||
while (my $l = <$fh>) {
|
|
||||||
$out = $out.$l;
|
|
||||||
}
|
|
||||||
close($fh);
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Read project from first argument
|
# Read project from first argument
|
||||||
sub Usage {
|
sub Usage {
|
||||||
die "Usage: $0 <OBS Project> [org [package]]";
|
die "Usage: $0 <OBS Project> [org [package]]";
|
||||||
@@ -89,7 +65,6 @@ sub Usage {
|
|||||||
|
|
||||||
my $project = shift or Usage();
|
my $project = shift or Usage();
|
||||||
my $org = shift;
|
my $org = shift;
|
||||||
|
|
||||||
if (not defined($org)) {
|
if (not defined($org)) {
|
||||||
$org = `osc meta prj $project | grep scmsync | sed -e 's,^.*src.opensuse.org/\\(.*\\)/_ObsPrj.*,\\1,'`;
|
$org = `osc meta prj $project | grep scmsync | sed -e 's,^.*src.opensuse.org/\\(.*\\)/_ObsPrj.*,\\1,'`;
|
||||||
chomp($org);
|
chomp($org);
|
||||||
@@ -164,7 +139,7 @@ if ( scalar @tomove > 0 ) {
|
|||||||
system("git -C $pkg push origin factory") == 0 and
|
system("git -C $pkg push origin factory") == 0 and
|
||||||
system("git obs $super_user api -X PATCH --data '{\"default_branch\": \"factory\"}' /repos/pool/$pkg") == 0
|
system("git obs $super_user api -X PATCH --data '{\"default_branch\": \"factory\"}' /repos/pool/$pkg") == 0
|
||||||
or die "Error in creating a pool repo";
|
or die "Error in creating a pool repo";
|
||||||
system("for i in \$(git -C $pkg for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin/ | grep -v '\\(^HEAD\$\\|^factory\$\\)'); do git -C $pkg push origin :\$i; done") == 0 or die "failed to cull branches";
|
system("for i in \$(git for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin/ | grep -v '\\(^HEAD\$\\|^factory\$\)'); do git -C $pkg push origin :\$i; done") == 0 or die "failed to cull branches";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +167,6 @@ for my $package ( sort(@packages) ) {
|
|||||||
or ( push( @tomove, $package ) and die "Can't fetch pool for $package" );
|
or ( push( @tomove, $package ) and die "Can't fetch pool for $package" );
|
||||||
|
|
||||||
my @commits = FindFactoryCommit($package);
|
my @commits = FindFactoryCommit($package);
|
||||||
my $Md5Hashes = FactoryMd5($package);
|
|
||||||
my $c;
|
my $c;
|
||||||
my $match = 0;
|
my $match = 0;
|
||||||
for my $commit (@commits) {
|
for my $commit (@commits) {
|
||||||
@@ -205,27 +179,16 @@ for my $package ( sort(@packages) ) {
|
|||||||
system("git -C $package lfs fetch pool $commit") == 0
|
system("git -C $package lfs fetch pool $commit") == 0
|
||||||
and system("git -C $package checkout -B factory $commit") == 0
|
and system("git -C $package checkout -B factory $commit") == 0
|
||||||
and system("git -C $package lfs checkout") == 0
|
and system("git -C $package lfs checkout") == 0
|
||||||
and chdir($package)) {
|
and system(
|
||||||
|
"cd $package; osc ls -v openSUSE:Factory $package | awk '{print \$1 FS \$7}' | grep -v -F '_scmsync.obsinfo\nbuild.specials.obscpio' | md5sum -c --quiet"
|
||||||
open(my $fh, "|-", "md5sum -c --quiet") or die $!;
|
) == 0
|
||||||
print $fh $Md5Hashes;
|
and system("bash -c \"diff <(ls -1 $package | sort) <(osc ls openSUSE:Factory $package | grep -v -F '_scmsync.obsinfo\nbuild.specials.obscpio' | sort)\"") == 0
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$c = $commit;
|
||||||
|
$match = 1;
|
||||||
|
last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,16 @@
|
|||||||
Java:packages
|
|
||||||
Kernel:firmware
|
Kernel:firmware
|
||||||
Kernel:kdump
|
Kernel:kdump
|
||||||
devel:gcc
|
|
||||||
devel:languages:clojure
|
devel:languages:clojure
|
||||||
devel:languages:erlang
|
devel:languages:erlang
|
||||||
devel:languages:erlang:Factory
|
devel:languages:erlang:Factory
|
||||||
devel:languages:hare
|
devel:languages:hare
|
||||||
devel:languages:javascript
|
devel:languages:javascript
|
||||||
devel:languages:lua
|
devel:languages:lua
|
||||||
devel:languages:nodejs
|
|
||||||
devel:languages:perl
|
devel:languages:perl
|
||||||
devel:languages:python:Factory
|
|
||||||
devel:languages:python:pytest
|
|
||||||
devel:openSUSE:Factory
|
devel:openSUSE:Factory
|
||||||
network:chromium
|
|
||||||
network:dhcp
|
network:dhcp
|
||||||
network:im:whatsapp
|
network:im:whatsapp
|
||||||
network:messaging:xmpp
|
network:messaging:xmpp
|
||||||
science:HPC
|
|
||||||
server:dns
|
server:dns
|
||||||
systemsmanagement:cockpit
|
systemsmanagement:cockpit
|
||||||
X11:lxde
|
X11:lxde
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ import (
|
|||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
Context string `json:"context"`
|
||||||
|
State string `json:"state"`
|
||||||
|
TargetUrl string `json:"target_url"`
|
||||||
|
}
|
||||||
|
|
||||||
type StatusInput struct {
|
type StatusInput struct {
|
||||||
Description string `json:"description"`
|
State string `json:"state"`
|
||||||
Context string `json:"context"`
|
TargetUrl string `json:"target_url"`
|
||||||
State string `json:"state"`
|
|
||||||
TargetUrl string `json:"target_url"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -55,26 +59,23 @@ func StatusProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
config, ok := r.Context().Value(configKey).(*Config)
|
config, ok := r.Context().Value(configKey).(*Config)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
common.LogDebug("Config missing from context")
|
common.LogError("Config missing from context")
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
header := r.Header.Get("Authorization")
|
header := r.Header.Get("Authorization")
|
||||||
if header == "" {
|
if header == "" {
|
||||||
common.LogDebug("Authorization header not found")
|
|
||||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
token_arr := strings.Split(header, " ")
|
token_arr := strings.Split(header, " ")
|
||||||
if len(token_arr) != 2 {
|
if len(token_arr) != 2 {
|
||||||
common.LogDebug("Authorization header malformed")
|
|
||||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.EqualFold(token_arr[0], "token") {
|
if !strings.EqualFold(token_arr[0], "Bearer") {
|
||||||
common.LogDebug("Token not found in Authorization header")
|
|
||||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -82,7 +83,6 @@ func StatusProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
token := token_arr[1]
|
token := token_arr[1]
|
||||||
|
|
||||||
if !slices.Contains(config.Keys, token) {
|
if !slices.Contains(config.Keys, token) {
|
||||||
common.LogDebug("Provided token is not known")
|
|
||||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -104,8 +104,13 @@ func StatusProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
status := Status{
|
||||||
|
Context: "Build in obs",
|
||||||
|
State: statusinput.State,
|
||||||
|
TargetUrl: statusinput.TargetUrl,
|
||||||
|
}
|
||||||
|
|
||||||
status_payload, err := json.Marshal(statusinput)
|
status_payload, err := json.Marshal(status)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
@@ -126,8 +131,8 @@ func StatusProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "Content-Type")
|
||||||
req.Header.Add("Authorization", fmt.Sprintf("token %s", ForgeToken))
|
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ForgeToken))
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
# gitea_status_proxy
|
|
||||||
|
|
||||||
Allows bots without code owner permission to set Gitea's commit status
|
|
||||||
|
|
||||||
## Basic usage
|
|
||||||
|
|
||||||
To beging, you need the json config and a Gitea token with permissions to the repository you want to write to.
|
|
||||||
|
|
||||||
Keys should be randomly generated, i.e by using openssl: `openssl rand -base64 48`
|
|
||||||
|
|
||||||
Generate a json config file, with the key generated from running the command above, save as example.json:
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"forge_url": "https://src.opensuse.org/api/v1",
|
|
||||||
"keys": ["$YOUR_TOKEN_GOES_HERE"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### start the proxy:
|
|
||||||
|
|
||||||
```
|
|
||||||
GITEA_TOKEN=YOURTOKEN ./gitea_status_proxy -config example.json
|
|
||||||
2025/10/30 12:53:18 [I] server up and listening on :3000
|
|
||||||
```
|
|
||||||
|
|
||||||
Now the proxy should be able to accept requests under: `localhost:3000/repos/{owner}/{repo}/statuses/{sha}`, the token to be used when authenticating to the proxy must be in the `keys` list of the configuration json file (example.json above)
|
|
||||||
|
|
||||||
### example:
|
|
||||||
|
|
||||||
On a separate terminal, you can use curl to post a status to the proxy, if the GITEA_TOKEN has permissions on the target
|
|
||||||
repository, it will result in a new status being set for the given commit
|
|
||||||
|
|
||||||
```
|
|
||||||
curl -X 'POST' \
|
|
||||||
'localhost:3000/repos/szarate/test-actions-gitea/statuses/cd5847c92fb65a628bdd6015f96ee7e569e1ad6e4fc487acc149b52e788262f9' \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H 'Authorization: token $YOUR_TOKEN_GOES_HERE' \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d '{
|
|
||||||
"context": "Proxy test",
|
|
||||||
"description": "Status posted from the proxy",
|
|
||||||
"state": "success",
|
|
||||||
"target_url": "https://src.opensuse.org"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
After this you should be able to the results in the pull request, e.g from above: https://src.opensuse.org/szarate/test-actions-gitea/pulls/1
|
|
||||||
@@ -39,16 +39,3 @@ Requirements
|
|||||||
+ R/W Notification
|
+ R/W Notification
|
||||||
+ R User
|
+ R User
|
||||||
|
|
||||||
Env Variables
|
|
||||||
-------------
|
|
||||||
The following variables can be used (and override) command line parameters.
|
|
||||||
|
|
||||||
* `AUTOGITS_CONFIG` - config file location
|
|
||||||
* `AUTOGITS_URL` - Gitea URL
|
|
||||||
* `AUTOGITS_RABBITURL` - RabbitMQ url
|
|
||||||
* `AUTOGITS_DEBUG` - when set, debug level logging enabled
|
|
||||||
|
|
||||||
Authentication env variables
|
|
||||||
* `GITEA_TOKEN` - Gitea user token
|
|
||||||
* `AMQP_USERNAME`, `AMQP_PASSWORD` - username and password for rabbitmq
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"slices"
|
"slices"
|
||||||
@@ -150,7 +149,7 @@ func ProcessNotifications(notification *models.NotificationThread, gitea common.
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
rx := regexp.MustCompile(`^/?api/v\d+/repos/(?<org>[_\.a-zA-Z0-9-]+)/(?<project>[_\.a-zA-Z0-9-]+)/(?:issues|pulls)/(?<num>[0-9]+)$`)
|
rx := regexp.MustCompile(`^/?api/v\d+/repos/(?<org>[_a-zA-Z0-9-]+)/(?<project>[_a-zA-Z0-9-]+)/(?:issues|pulls)/(?<num>[0-9]+)$`)
|
||||||
subject := notification.Subject
|
subject := notification.Subject
|
||||||
u, err := url.Parse(notification.Subject.URL)
|
u, err := url.Parse(notification.Subject.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -253,7 +252,7 @@ func ProcessPR(pr *models.PullRequest) error {
|
|||||||
if !common.IsDryRun {
|
if !common.IsDryRun {
|
||||||
text := reviewer + " requested changes on behalf of " + groupName + ". See " + review.HTMLURL
|
text := reviewer + " requested changes on behalf of " + groupName + ". See " + review.HTMLURL
|
||||||
if review := FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
if review := FindOurLastReviewInTimeline(timeline); review == nil || review.Body != text {
|
||||||
_, err := gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, text)
|
_, err := gitea.AddReviewComment(pr, common.ReviewStateRequestChanges, "Changes requested. See review by: "+reviewer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(" -> failed to write rejecting comment", err)
|
common.LogError(" -> failed to write rejecting comment", err)
|
||||||
}
|
}
|
||||||
@@ -329,24 +328,6 @@ func main() {
|
|||||||
flag.BoolVar(&common.IsDryRun, "dry", false, "Dry run, no effect. For debugging")
|
flag.BoolVar(&common.IsDryRun, "dry", false, "Dry run, no effect. For debugging")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if err := common.SetLoggingLevelFromString(*logging); err != nil {
|
|
||||||
common.LogError(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if cf := os.Getenv("AUTOGITS_CONFIG"); len(cf) > 0 {
|
|
||||||
*configFile = cf
|
|
||||||
}
|
|
||||||
if url := os.Getenv("AUTOGITS_URL"); len(url) > 0 {
|
|
||||||
*giteaUrl = url
|
|
||||||
}
|
|
||||||
if url := os.Getenv("AUTOGITS_RABBITURL"); len(url) > 0 {
|
|
||||||
*rabbitMqHost = url
|
|
||||||
}
|
|
||||||
if debug := os.Getenv("AUTOGITS_DEBUG"); len(debug) > 0 {
|
|
||||||
common.SetLoggingLevel(common.LogLevelDebug)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
log.Println(" syntax:")
|
log.Println(" syntax:")
|
||||||
@@ -391,6 +372,11 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := common.SetLoggingLevelFromString(*logging); err != nil {
|
||||||
|
common.LogError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if *interval < 1 {
|
if *interval < 1 {
|
||||||
*interval = 1
|
*interval = 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,28 +386,6 @@ func CreateQASubProject(stagingConfig *common.StagingConfig, git common.Git, git
|
|||||||
}
|
}
|
||||||
// patch baseMeta to become the new project
|
// patch baseMeta to become the new project
|
||||||
templateMeta.Name = stagingProject + ":" + subProjectName
|
templateMeta.Name = stagingProject + ":" + subProjectName
|
||||||
// freeze tag for now
|
|
||||||
if len(templateMeta.ScmSync) > 0 {
|
|
||||||
repository, err := url.Parse(templateMeta.ScmSync)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
common.LogDebug("getting data for ", repository.EscapedPath())
|
|
||||||
split := strings.Split(repository.EscapedPath(), "/")
|
|
||||||
org, repo := split[1], split[2]
|
|
||||||
|
|
||||||
common.LogDebug("getting commit for ", org, " repo ", repo, " fragment ", repository.Fragment)
|
|
||||||
branch, err := gitea.GetCommit(org, repo, repository.Fragment)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set expanded commit url
|
|
||||||
repository.Fragment = branch.SHA
|
|
||||||
templateMeta.ScmSync = repository.String()
|
|
||||||
common.LogDebug("Setting scmsync url to ", templateMeta.ScmSync)
|
|
||||||
}
|
|
||||||
// Cleanup ReleaseTarget and modify affected path entries
|
// Cleanup ReleaseTarget and modify affected path entries
|
||||||
for idx, r := range templateMeta.Repositories {
|
for idx, r := range templateMeta.Repositories {
|
||||||
templateMeta.Repositories[idx].ReleaseTargets = nil
|
templateMeta.Repositories[idx].ReleaseTargets = nil
|
||||||
@@ -1066,7 +1044,6 @@ func main() {
|
|||||||
ObsWebHost = ObsWebHostFromApiHost(*obsApiHost)
|
ObsWebHost = ObsWebHostFromApiHost(*obsApiHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogDebug("OBS Gitea Host:", GiteaUrl)
|
|
||||||
common.LogDebug("OBS Web Host:", ObsWebHost)
|
common.LogDebug("OBS Web Host:", ObsWebHost)
|
||||||
common.LogDebug("OBS API Host:", *obsApiHost)
|
common.LogDebug("OBS API Host:", *obsApiHost)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
OBS Status Service
|
OBS Status Service
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Reports build status of OBS service as an easily to produce SVG. Repository
|
Reports build status of OBS service as an easily to produce SVG
|
||||||
results (build results) are cached for 10 seconds and repository listing
|
|
||||||
for OBS instance are cached for 5 minutes -- new repositories take up to
|
|
||||||
5 minutes to be visible.
|
|
||||||
|
|
||||||
Requests for individual build results:
|
Requests for individual build results:
|
||||||
|
|
||||||
@@ -20,31 +17,19 @@ Get requests for / will also return 404 statu normally. If the Backend redis
|
|||||||
server is not available, it will return 500
|
server is not available, it will return 500
|
||||||
|
|
||||||
|
|
||||||
By default, SVG output is generated, suitable for inclusion. But JSON and XML
|
|
||||||
output is possible by setting `Accept:` request header
|
|
||||||
|
|
||||||
| Accept Request Header | Output format
|
|
||||||
|------------------------|---------------------
|
|
||||||
| | SVG image
|
|
||||||
| application/json | JSON data
|
|
||||||
| application/obs+xml | XML output
|
|
||||||
|
|
||||||
|
|
||||||
Areas of Responsibility
|
Areas of Responsibility
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
* Fetch and cache internal data from OBS and present it in usable format:
|
* Monitors RabbitMQ interface for notification of OBS package and project status
|
||||||
+ Generate SVG output for specific OBS project or package
|
* Produces SVG output based on GET request
|
||||||
+ Generate JSON/XML output for automated processing
|
* Cache results (sqlite) and periodically update results from OBS (in case of messages are missing)
|
||||||
* Low-overhead
|
|
||||||
|
|
||||||
|
|
||||||
Target Usage
|
Target Usage
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* inside README.md of package git or project git
|
* README.md of package git or project git
|
||||||
* comment section of a Gitea PR
|
* comment section of a Gitea PR
|
||||||
* automated build result processing
|
|
||||||
|
|
||||||
Running
|
Running
|
||||||
-------
|
-------
|
||||||
@@ -57,4 +42,3 @@ Default parameters can be changed by env variables
|
|||||||
| `OBS_STATUS_SERVICE_LISTEN` | [::1]:8080 | Listening address and port
|
| `OBS_STATUS_SERVICE_LISTEN` | [::1]:8080 | Listening address and port
|
||||||
| `OBS_STATUS_SERVICE_CERT` | /run/obs-status-service.pem | Location of certificate file for service
|
| `OBS_STATUS_SERVICE_CERT` | /run/obs-status-service.pem | Location of certificate file for service
|
||||||
| `OBS_STATUS_SERVICE_KEY` | /run/obs-status-service.pem | Location of key file for service
|
| `OBS_STATUS_SERVICE_KEY` | /run/obs-status-service.pem | Location of key file for service
|
||||||
| `REDIS` | | OBS's Redis instance URL
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"compress/bzip2"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -85,36 +82,3 @@ func TestStatusSvg(t *testing.T) {
|
|||||||
os.WriteFile("testpackage.svg", PackageStatusSummarySvg("pkg2", data), 0o777)
|
os.WriteFile("testpackage.svg", PackageStatusSummarySvg("pkg2", data), 0o777)
|
||||||
os.WriteFile("testproject.svg", ProjectStatusSummarySvg(data), 0o777)
|
os.WriteFile("testproject.svg", ProjectStatusSummarySvg(data), 0o777)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFactoryResults(t *testing.T) {
|
|
||||||
data, err := os.Open("factory.results.json.bz2")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Openning factory.results.json.bz2 failed:", err)
|
|
||||||
}
|
|
||||||
UncompressedData, err := io.ReadAll(bzip2.NewReader(data))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Reading factory.results.json.bz2 failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var results []*common.BuildResult
|
|
||||||
if err := json.Unmarshal(UncompressedData, &results); err != nil {
|
|
||||||
t.Fatal("Failed parsing test data", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add tests here
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
}{
|
|
||||||
// add test data here
|
|
||||||
{
|
|
||||||
name: "First test",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// and test code here
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"html"
|
"html"
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SvgWriter struct {
|
type SvgWriter struct {
|
||||||
@@ -134,7 +133,7 @@ func (svg *SvgWriter) WritePackageStatus(loglink, arch, status, detail string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (svg *SvgWriter) WriteProjectStatus(project, repo, arch, status string, count int) {
|
func (svg *SvgWriter) WriteProjectStatus(project, repo, arch, status string, count int) {
|
||||||
u, err := url.Parse(*ObsUrl + "/project/monitor/" + url.PathEscape(project) + "?defaults=0&" + url.QueryEscape(status) + "=1&arch_" + url.QueryEscape(arch) + "=1&repo_" + url.QueryEscape(strings.ReplaceAll(repo, ".", "_")) + "=1")
|
u, err := url.Parse(*ObsUrl + "/project/monitor/" + url.PathEscape(project) + "?defaults=0&" + url.QueryEscape(status) + "=1&arch_" + url.QueryEscape(arch) + "=1&repo_" + url.QueryEscape(repo) + "=1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Group Review bot for %i
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=exec
|
|
||||||
ExecStart=/usr/bin/group-review %i
|
|
||||||
EnvironmentFile=-/etc/default/group-review/%i.env
|
|
||||||
DynamicUser=yes
|
|
||||||
NoNewPrivileges=yes
|
|
||||||
ProtectSystem=strict
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=WorkflowDirect git bot for %i
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=exec
|
|
||||||
ExecStart=/usr/bin/workflow-direct
|
|
||||||
EnvironmentFile=-/etc/default/%i/workflow-direct.env
|
|
||||||
DynamicUser=yes
|
|
||||||
NoNewPrivileges=yes
|
|
||||||
ProtectSystem=strict
|
|
||||||
RuntimeDirectory=%i
|
|
||||||
# SLES 15 doesn't have HOME set for dynamic users, so we improvise
|
|
||||||
BindReadOnlyPaths=/etc/default/%i/known_hosts:/etc/ssh/ssh_known_hosts /etc/default/%i/config.json:%t/%i/config.json /etc/default/%i/id_ed25519 /etc/default/%i/id_ed25519.pub
|
|
||||||
WorkingDirectory=%t/%i
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|
||||||
9
utils/gitmodules-automerge/README.md
Normal file
9
utils/gitmodules-automerge/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Purpose
|
||||||
|
-------
|
||||||
|
|
||||||
|
Automatically resolve git configlicts in .gitmodules if there's a conflict
|
||||||
|
due to a merge.
|
||||||
|
|
||||||
|
It uses HEAD and MERGE_HEAD to calculate merge base and pass it to the
|
||||||
|
conflict resolution
|
||||||
|
|
||||||
42
utils/gitmodules-automerge/main.go
Normal file
42
utils/gitmodules-automerge/main.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"src.opensuse.org/autogits/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gh, err := common.AllocateGitWorkTree(cwd, "", "")
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
git, err := gh.ReadExistingPath("")
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeBase := strings.TrimSpace(git.GitExecWithOutputOrPanic("", "merge-base", "HEAD", "MERGE_HEAD"))
|
||||||
|
status, err := git.GitStatus("")
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range status {
|
||||||
|
if s.Path == ".gitmodules" && s.Status == common.GitStatus_Unmerged {
|
||||||
|
if err := git.GitResolveSubmoduleFileConflict(s, "", MergeBase, "HEAD", "MERGE_HEAD"); err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
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")
|
|
||||||
)
|
|
||||||
|
|
||||||
const maintainershipFile = "_maintainership.json"
|
|
||||||
|
|
||||||
func WriteNewMaintainershipFile(m *common.MaintainershipMap, filename string) {
|
|
||||||
f, err := os.Create(filename + ".new")
|
|
||||||
common.PanicOnError(err)
|
|
||||||
common.PanicOnError(m.WriteMaintainershipFile(f))
|
|
||||||
common.PanicOnError(f.Close())
|
|
||||||
common.PanicOnError(os.Rename(filename+".new", filename))
|
|
||||||
}
|
|
||||||
|
|
||||||
func run() error {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
filename := 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,243 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
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{},
|
|
||||||
expectedError: "no such file or directory",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid json",
|
|
||||||
inData: `{"pkg1": ["user1"`,
|
|
||||||
params: []string{},
|
|
||||||
expectedError: "Failed to parse JSON",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "add and remove",
|
|
||||||
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,
|
|
||||||
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 := 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)
|
|
||||||
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")
|
|
||||||
|
|
||||||
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(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(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,9 @@ Areas of responsibility
|
|||||||
* on repository adds, creates a new submodule (if non empty)
|
* on repository adds, creates a new submodule (if non empty)
|
||||||
* on repository removal, removes the submodule
|
* on repository removal, removes the submodule
|
||||||
|
|
||||||
|
NOTE: reverts (push HEAD^) are not supported as they would step-on the
|
||||||
|
work of the workflow-pr bot. Manual update of the project git is
|
||||||
|
required in this case.
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
@@ -23,20 +26,6 @@ Uses `workflow.config` for configuration. Parameters
|
|||||||
|
|
||||||
NOTE: `-rm`, `-removed`, `-deleted` are all removed suffixes used to indicate current branch is a placeholder for previously existing package. These branches will be ignored by the bot, and if default, the package will be removed and will not be added to the project.
|
NOTE: `-rm`, `-removed`, `-deleted` are all removed suffixes used to indicate current branch is a placeholder for previously existing package. These branches will be ignored by the bot, and if default, the package will be removed and will not be added to the project.
|
||||||
|
|
||||||
Running
|
|
||||||
-------
|
|
||||||
|
|
||||||
* `GITEA_TOKEN` (required)
|
|
||||||
* `AMQP_USERNAME` (required)
|
|
||||||
* `AMQP_PASSWORD` (required)
|
|
||||||
* `AUTOGITS_CONFIG` (required)
|
|
||||||
* `AUTOGITS_URL` - default: https://src.opensuse.org
|
|
||||||
* `AUTOGITS_RABBITURL` - default: amqps://rabbit.opensuse.org
|
|
||||||
* `AUTOGITS_DEBUG` - disabled by default, set to any value to enable
|
|
||||||
* `AUTOGITS_CHECK_ON_START` - disabled by default, set to any value to enable
|
|
||||||
* `AUTOGITS_REPO_PATH` - default is temporary directory
|
|
||||||
* `AUTOGITS_IDENTITY_FILE` - in case where we need explicit identify path for ssh specified
|
|
||||||
|
|
||||||
Target Usage
|
Target Usage
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -39,7 +40,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
AppName = "direct_workflow"
|
AppName = "direct_workflow"
|
||||||
GitAuthor = "AutoGits prjgit-updater"
|
GitAuthor = "AutoGits prjgit-updater"
|
||||||
GitEmail = "autogits-direct@noreply@src.opensuse.org"
|
GitEmail = "adam+autogits-direct@zombino.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
var configuredRepos map[string][]*common.AutogitConfig
|
var configuredRepos map[string][]*common.AutogitConfig
|
||||||
@@ -52,6 +53,18 @@ func isConfiguredOrg(org *common.Organization) bool {
|
|||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func concatenateErrors(err1, err2 error) error {
|
||||||
|
if err1 == nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
if err2 == nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%w\n%w", err1, err2)
|
||||||
|
}
|
||||||
|
|
||||||
type RepositoryActionProcessor struct{}
|
type RepositoryActionProcessor struct{}
|
||||||
|
|
||||||
func (*RepositoryActionProcessor) ProcessFunc(request *common.Request) error {
|
func (*RepositoryActionProcessor) ProcessFunc(request *common.Request) error {
|
||||||
@@ -59,90 +72,69 @@ func (*RepositoryActionProcessor) ProcessFunc(request *common.Request) error {
|
|||||||
configs, configFound := configuredRepos[action.Organization.Username]
|
configs, configFound := configuredRepos[action.Organization.Username]
|
||||||
|
|
||||||
if !configFound {
|
if !configFound {
|
||||||
common.LogInfo("Repository event for", action.Organization.Username, ". Not configured. Ignoring.", action.Organization.Username)
|
log.Printf("Repository event for %s. Not configured. Ignoring.\n", action.Organization.Username)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
if org, repo, _ := config.GetPrjGit(); org == action.Repository.Owner.Username && repo == action.Repository.Name {
|
if org, repo, _ := config.GetPrjGit(); org == action.Repository.Owner.Username && repo == action.Repository.Name {
|
||||||
common.LogError("+ ignoring repo event for PrjGit repository", config.GitProjectName)
|
log.Println("+ ignoring repo event for PrjGit repository", config.GitProjectName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
processConfiguredRepositoryAction(action, config)
|
err = concatenateErrors(err, processConfiguredRepositoryAction(action, config))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func processConfiguredRepositoryAction(action *common.RepositoryWebhookEvent, config *common.AutogitConfig) {
|
func processConfiguredRepositoryAction(action *common.RepositoryWebhookEvent, config *common.AutogitConfig) error {
|
||||||
gitOrg, gitPrj, gitBranch := config.GetPrjGit()
|
gitOrg, gitPrj, gitBranch := config.GetPrjGit()
|
||||||
git, err := gh.CreateGitHandler(config.Organization)
|
git, err := gh.CreateGitHandler(config.Organization)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
defer git.Close()
|
defer git.Close()
|
||||||
|
|
||||||
configBranch := config.Branch
|
if len(config.Branch) == 0 {
|
||||||
if len(configBranch) == 0 {
|
config.Branch = action.Repository.Default_Branch
|
||||||
configBranch = action.Repository.Default_Branch
|
|
||||||
if common.IsRemovedBranch(configBranch) {
|
|
||||||
common.LogDebug(" - default branch has deleted suffix. Skipping")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(configBranch) == 0 {
|
|
||||||
common.LogDebug("Empty default branch in message. Maybe race-condition?")
|
|
||||||
repo, err := gitea.GetRepository(action.Repository.Owner.Username, action.Repository.Name)
|
|
||||||
if err != nil {
|
|
||||||
common.LogError("Failed to fetch repository we have an event for?", action.Repository.Owner.Username, action.Repository.Name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(repo.DefaultBranch) == 0 {
|
|
||||||
common.LogError("Default branch is somehow empty. We cannot do anything.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
configBranch = repo.DefaultBranch
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prjGitRepo, err := gitea.CreateRepositoryIfNotExist(git, gitOrg, gitPrj)
|
prjGitRepo, err := gitea.CreateRepositoryIfNotExist(git, gitOrg, gitPrj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Error accessing/creating prjgit:", gitOrg, gitPrj, gitBranch, err)
|
return fmt.Errorf("Error accessing/creating prjgit: %s/%s#%s err: %w", gitOrg, gitPrj, gitBranch, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteName, err := git.GitClone(gitPrj, gitBranch, prjGitRepo.SSHURL)
|
remoteName, err := git.GitClone(gitPrj, gitBranch, prjGitRepo.SSHURL)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
|
|
||||||
|
|
||||||
switch action.Action {
|
switch action.Action {
|
||||||
case "created":
|
case "created":
|
||||||
if action.Repository.Object_Format_Name != "sha256" {
|
if action.Repository.Object_Format_Name != "sha256" {
|
||||||
common.LogError(" - ", action.Repository.Name, "repo is not sha256. Ignoring.")
|
return fmt.Errorf(" - '%s' repo is not sha256. Ignoring.", action.Repository.Name)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name))
|
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name))
|
||||||
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
|
defer git.GitExecOrPanic(gitPrj, "submodule", "deinit", "--all")
|
||||||
|
|
||||||
branch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, action.Repository.Name), "branch", "--show-current"))
|
branch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, action.Repository.Name), "branch", "--show-current"))
|
||||||
if branch != configBranch {
|
if branch != config.Branch {
|
||||||
if err := git.GitExec(path.Join(gitPrj, action.Repository.Name), "fetch", "--depth", "1", "origin", configBranch+":"+configBranch); err != nil {
|
if err := git.GitExec(path.Join(gitPrj, action.Repository.Name), "fetch", "--depth", "1", "origin", config.Branch+":"+config.Branch); err != nil {
|
||||||
common.LogError("error fetching branch", configBranch, ". ignoring as non-existent.", err) // no branch? so ignore repo here
|
return fmt.Errorf("error fetching branch %s. ignoring as non-existent. err: %w", config.Branch, err) // no branch? so ignore repo here
|
||||||
return
|
|
||||||
}
|
}
|
||||||
common.PanicOnError(git.GitExec(path.Join(gitPrj, action.Repository.Name), "checkout", configBranch))
|
common.PanicOnError(git.GitExec(path.Join(gitPrj, action.Repository.Name), "checkout", config.Branch))
|
||||||
}
|
}
|
||||||
common.PanicOnError(git.GitExec(gitPrj, "commit", "-m", "Auto-inclusion "+action.Repository.Name))
|
common.PanicOnError(git.GitExec(gitPrj, "commit", "-m", "Automatic package inclusion via Direct Workflow"))
|
||||||
if !noop {
|
if !noop {
|
||||||
common.PanicOnError(git.GitExec(gitPrj, "push"))
|
common.PanicOnError(git.GitExec(gitPrj, "push"))
|
||||||
}
|
}
|
||||||
|
|
||||||
case "deleted":
|
case "deleted":
|
||||||
if stat, err := os.Stat(filepath.Join(git.GetPath(), gitPrj, action.Repository.Name)); err != nil || !stat.IsDir() {
|
if stat, err := os.Stat(filepath.Join(git.GetPath(), gitPrj, action.Repository.Name)); err != nil || !stat.IsDir() {
|
||||||
common.LogDebug("delete event for", action.Repository.Name, "-- not in project. Ignoring")
|
if DebugMode {
|
||||||
return
|
log.Println("delete event for", action.Repository.Name, "-- not in project. Ignoring")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
common.PanicOnError(git.GitExec(gitPrj, "rm", action.Repository.Name))
|
common.PanicOnError(git.GitExec(gitPrj, "rm", action.Repository.Name))
|
||||||
common.PanicOnError(git.GitExec(gitPrj, "commit", "-m", "Automatic package removal via Direct Workflow"))
|
common.PanicOnError(git.GitExec(gitPrj, "commit", "-m", "Automatic package removal via Direct Workflow"))
|
||||||
@@ -151,9 +143,10 @@ func processConfiguredRepositoryAction(action *common.RepositoryWebhookEvent, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
common.LogError("Unknown action type:", action.Action)
|
return fmt.Errorf("%s: %s", "Unknown action type", action.Action)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type PushActionProcessor struct{}
|
type PushActionProcessor struct{}
|
||||||
@@ -163,83 +156,77 @@ func (*PushActionProcessor) ProcessFunc(request *common.Request) error {
|
|||||||
configs, configFound := configuredRepos[action.Repository.Owner.Username]
|
configs, configFound := configuredRepos[action.Repository.Owner.Username]
|
||||||
|
|
||||||
if !configFound {
|
if !configFound {
|
||||||
common.LogDebug("Repository event for", action.Repository.Owner.Username, ". Not configured. Ignoring.")
|
log.Printf("Repository event for %s. Not configured. Ignoring.\n", action.Repository.Owner.Username)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
if gitOrg, gitPrj, _ := config.GetPrjGit(); gitOrg == action.Repository.Owner.Username && gitPrj == action.Repository.Name {
|
if gitOrg, gitPrj, _ := config.GetPrjGit(); gitOrg == action.Repository.Owner.Username && gitPrj == action.Repository.Name {
|
||||||
common.LogInfo("+ ignoring push to PrjGit repository", config.GitProjectName)
|
log.Println("+ ignoring push to PrjGit repository", config.GitProjectName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
processConfiguredPushAction(action, config)
|
err = concatenateErrors(err, processConfiguredPushAction(action, config))
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func processConfiguredPushAction(action *common.PushWebhookEvent, config *common.AutogitConfig) {
|
func processConfiguredPushAction(action *common.PushWebhookEvent, config *common.AutogitConfig) error {
|
||||||
gitOrg, gitPrj, gitBranch := config.GetPrjGit()
|
gitOrg, gitPrj, gitBranch := config.GetPrjGit()
|
||||||
git, err := gh.CreateGitHandler(config.Organization)
|
git, err := gh.CreateGitHandler(config.Organization)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
defer git.Close()
|
defer git.Close()
|
||||||
|
|
||||||
common.LogDebug("push to:", action.Repository.Owner.Username, action.Repository.Name, "for:", gitOrg, gitPrj, gitBranch)
|
log.Printf("push to: %s/%s for %s/%s#%s", action.Repository.Owner.Username, action.Repository.Name, gitOrg, gitPrj, gitBranch)
|
||||||
branch := config.Branch
|
if len(config.Branch) == 0 {
|
||||||
if len(branch) == 0 {
|
config.Branch = action.Repository.Default_Branch
|
||||||
if common.IsRemovedBranch(branch) {
|
log.Println(" + default branch", action.Repository.Default_Branch)
|
||||||
common.LogDebug(" + default branch has removed suffix:", branch, "Skipping.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
branch = action.Repository.Default_Branch
|
|
||||||
common.LogDebug(" + using default branch", branch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prjGitRepo, err := gitea.CreateRepositoryIfNotExist(git, gitOrg, gitPrj)
|
prjGitRepo, err := gitea.CreateRepositoryIfNotExist(git, gitOrg, gitPrj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Error accessing/creating prjgit:", gitOrg, gitPrj, err)
|
return fmt.Errorf("Error accessing/creating prjgit: %s/%s err: %w", gitOrg, gitPrj, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteName, err := git.GitClone(gitPrj, gitBranch, prjGitRepo.SSHURL)
|
remoteName, err := git.GitClone(gitPrj, gitBranch, prjGitRepo.SSHURL)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
|
|
||||||
headCommitId, err := git.GitRemoteHead(gitPrj, remoteName, gitBranch)
|
headCommitId, err := git.GitRemoteHead(gitPrj, remoteName, gitBranch)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
commit, ok := git.GitSubmoduleCommitId(gitPrj, action.Repository.Name, headCommitId)
|
commit, ok := git.GitSubmoduleCommitId(gitPrj, action.Repository.Name, headCommitId)
|
||||||
for ok && action.Head_Commit.Id == commit {
|
for ok && action.Head_Commit.Id == commit {
|
||||||
common.LogDebug(" -- nothing to do, commit already in ProjectGit")
|
log.Println(" -- nothing to do, commit already in ProjectGit")
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if stat, err := os.Stat(filepath.Join(git.GetPath(), gitPrj, action.Repository.Name)); err != nil {
|
if stat, err := os.Stat(filepath.Join(git.GetPath(), gitPrj, action.Repository.Name)); err != nil || !stat.IsDir() {
|
||||||
git.GitExecOrPanic(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name)
|
if DebugMode {
|
||||||
common.LogDebug("Pushed to package that is not part of the project. Re-adding...", err)
|
log.Println("Pushed to package that is not part of the project. Ignoring:", err)
|
||||||
} else if !stat.IsDir() {
|
}
|
||||||
common.LogError("Pushed to a package that is not a submodule but exists in the project. Ignoring.")
|
return nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
git.GitExecOrPanic(gitPrj, "submodule", "update", "--init", "--force", "--depth", "1", "--checkout", action.Repository.Name)
|
git.GitExecOrPanic(gitPrj, "submodule", "update", "--init", "--depth", "1", "--checkout", action.Repository.Name)
|
||||||
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
|
defer git.GitExecOrPanic(gitPrj, "submodule", "deinit", "--all")
|
||||||
|
|
||||||
if err := git.GitExec(filepath.Join(gitPrj, action.Repository.Name), "fetch", "--depth", "1", "--force", "origin", branch+":"+branch); err != nil {
|
if err := git.GitExec(filepath.Join(gitPrj, action.Repository.Name), "fetch", "--depth", "1", "--force", remoteName, config.Branch+":"+config.Branch); err != nil {
|
||||||
common.LogError("Error fetching branch:", branch, "Ignoring as non-existent.", err)
|
return fmt.Errorf("error fetching branch %s. ignoring as non-existent. err: %w", config.Branch, err) // no branch? so ignore repo here
|
||||||
return
|
|
||||||
}
|
}
|
||||||
id, err := git.GitBranchHead(filepath.Join(gitPrj, action.Repository.Name), branch)
|
id, err := git.GitRemoteHead(filepath.Join(gitPrj, action.Repository.Name), remoteName, config.Branch)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
if action.Head_Commit.Id == id {
|
if action.Head_Commit.Id == id {
|
||||||
git.GitExecOrPanic(filepath.Join(gitPrj, action.Repository.Name), "checkout", id)
|
git.GitExecOrPanic(filepath.Join(gitPrj, action.Repository.Name), "checkout", id)
|
||||||
git.GitExecOrPanic(gitPrj, "commit", "-a", "-m", fmt.Sprintf("'%s' update via Direct Workflow", action.Repository.Name))
|
git.GitExecOrPanic(gitPrj, "commit", "-a", "-m", "Automatic update via push via Direct Workflow")
|
||||||
if !noop {
|
if !noop {
|
||||||
git.GitExecOrPanic(gitPrj, "push", remoteName)
|
git.GitExecOrPanic(gitPrj, "push", remoteName)
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogDebug("push of refs not on the configured branch", branch, ". ignoring.")
|
log.Println("push of refs not on the configured branch", config.Branch, ". ignoring.")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyProjectState(git common.Git, org string, config *common.AutogitConfig, configs []*common.AutogitConfig) (err error) {
|
func verifyProjectState(git common.Git, org string, config *common.AutogitConfig, configs []*common.AutogitConfig) (err error) {
|
||||||
@@ -261,65 +248,51 @@ func verifyProjectState(git common.Git, org string, config *common.AutogitConfig
|
|||||||
|
|
||||||
remoteName, err := git.GitClone(gitPrj, gitBranch, repo.SSHURL)
|
remoteName, err := git.GitClone(gitPrj, gitBranch, repo.SSHURL)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
|
defer git.GitExecOrPanic(gitPrj, "submodule", "deinit", "--all")
|
||||||
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
|
|
||||||
|
|
||||||
common.LogDebug(" * Getting submodule list")
|
log.Println(" * Getting submodule list")
|
||||||
sub, err := git.GitSubmoduleList(gitPrj, "HEAD")
|
sub, err := git.GitSubmoduleList(gitPrj, "HEAD")
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
|
|
||||||
common.LogDebug(" * Getting package links")
|
log.Println(" * Getting package links")
|
||||||
var pkgLinks []*PackageRebaseLink
|
var pkgLinks []*PackageRebaseLink
|
||||||
if f, err := fs.Stat(os.DirFS(path.Join(git.GetPath(), gitPrj)), common.PrjLinksFile); err == nil && (f.Mode()&fs.ModeType == 0) && f.Size() < 1000000 {
|
if f, err := fs.Stat(os.DirFS(path.Join(git.GetPath(), gitPrj)), common.PrjLinksFile); err == nil && (f.Mode()&fs.ModeType == 0) && f.Size() < 1000000 {
|
||||||
if data, err := os.ReadFile(path.Join(git.GetPath(), gitPrj, common.PrjLinksFile)); err == nil {
|
if data, err := os.ReadFile(path.Join(git.GetPath(), gitPrj, common.PrjLinksFile)); err == nil {
|
||||||
pkgLinks, err = parseProjectLinks(data)
|
pkgLinks, err = parseProjectLinks(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Cannot parse project links file:", err.Error())
|
log.Println("Cannot parse project links file:", err.Error())
|
||||||
pkgLinks = nil
|
pkgLinks = nil
|
||||||
} else {
|
} else {
|
||||||
ResolveLinks(org, pkgLinks, gitea)
|
ResolveLinks(org, pkgLinks, gitea)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
common.LogInfo(" - No package links defined")
|
log.Println(" - No package links defined")
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check existing submodule that they are updated */
|
/* Check existing submodule that they are updated */
|
||||||
|
|
||||||
isGitUpdated := false
|
isGitUpdated := false
|
||||||
next_package:
|
next_package:
|
||||||
for filename, commitId := range sub {
|
for filename, commitId := range sub {
|
||||||
// ignore project gits
|
// ignore project gits
|
||||||
//for _, c := range configs {
|
//for _, c := range configs {
|
||||||
if gitPrj == filename {
|
if gitPrj == filename {
|
||||||
common.LogDebug(" prjgit as package? ignoring project git:", filename)
|
log.Println(" prjgit as package? ignoring project git:", filename)
|
||||||
continue next_package
|
continue next_package
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
branch := config.Branch
|
log.Printf(" verifying package: %s -> %s(%s)", commitId, filename, config.Branch)
|
||||||
common.LogDebug(" verifying package:", commitId, "->", filename, "@", branch)
|
commits, err := gitea.GetRecentCommits(org, filename, config.Branch, 10)
|
||||||
if repo, err := gitea.GetRepository(org, filename); repo == nil && err == nil {
|
if len(commits) == 0 {
|
||||||
common.LogDebug(" repository removed...")
|
if repo, err := gitea.GetRepository(org, filename); repo == nil && err == nil {
|
||||||
git.GitExecOrPanic(gitPrj, "rm", filename)
|
|
||||||
isGitUpdated = true
|
|
||||||
continue
|
|
||||||
} else if err != nil {
|
|
||||||
common.LogError("failed fetching repo data", org, filename, err)
|
|
||||||
continue
|
|
||||||
} else if len(branch) == 0 {
|
|
||||||
branch = repo.DefaultBranch
|
|
||||||
common.LogDebug(" -> using default branch", branch)
|
|
||||||
if common.IsRemovedBranch(branch) {
|
|
||||||
common.LogDebug(" Default branch for", filename, "is excluded")
|
|
||||||
git.GitExecOrPanic(gitPrj, "rm", filename)
|
git.GitExecOrPanic(gitPrj, "rm", filename)
|
||||||
isGitUpdated = true
|
isGitUpdated = true
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commits, err := gitea.GetRecentCommits(org, filename, branch, 10)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogDebug(" -> failed to fetch recent commits for package:", filename, " Err:", err)
|
log.Println(" -> failed to fetch recent commits for package:", filename, " Err:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,7 +309,7 @@ next_package:
|
|||||||
if l.Pkg == filename {
|
if l.Pkg == filename {
|
||||||
link = l
|
link = l
|
||||||
|
|
||||||
common.LogDebug(" -> linked package")
|
log.Println(" -> linked package")
|
||||||
// so, we need to rebase here. Can't really optimize, so clone entire package tree and remote
|
// so, we need to rebase here. Can't really optimize, so clone entire package tree and remote
|
||||||
pkgPath := path.Join(gitPrj, filename)
|
pkgPath := path.Join(gitPrj, filename)
|
||||||
git.GitExecOrPanic(gitPrj, "submodule", "update", "--init", "--checkout", filename)
|
git.GitExecOrPanic(gitPrj, "submodule", "update", "--init", "--checkout", filename)
|
||||||
@@ -350,7 +323,7 @@ next_package:
|
|||||||
nCommits := len(common.SplitStringNoEmpty(git.GitExecWithOutputOrPanic(pkgPath, "rev-list", "^NOW", "HEAD"), "\n"))
|
nCommits := len(common.SplitStringNoEmpty(git.GitExecWithOutputOrPanic(pkgPath, "rev-list", "^NOW", "HEAD"), "\n"))
|
||||||
if nCommits > 0 {
|
if nCommits > 0 {
|
||||||
if !noop {
|
if !noop {
|
||||||
git.GitExecOrPanic(pkgPath, "push", "-f", "origin", "HEAD:"+branch)
|
git.GitExecOrPanic(pkgPath, "push", "-f", "origin", "HEAD:"+config.Branch)
|
||||||
}
|
}
|
||||||
isGitUpdated = true
|
isGitUpdated = true
|
||||||
}
|
}
|
||||||
@@ -367,27 +340,42 @@ next_package:
|
|||||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "update", "--init", "--depth", "1", "--checkout", filename))
|
common.PanicOnError(git.GitExec(gitPrj, "submodule", "update", "--init", "--depth", "1", "--checkout", filename))
|
||||||
common.PanicOnError(git.GitExec(filepath.Join(gitPrj, filename), "fetch", "--depth", "1", "origin", commits[0].SHA))
|
common.PanicOnError(git.GitExec(filepath.Join(gitPrj, filename), "fetch", "--depth", "1", "origin", commits[0].SHA))
|
||||||
common.PanicOnError(git.GitExec(filepath.Join(gitPrj, filename), "checkout", commits[0].SHA))
|
common.PanicOnError(git.GitExec(filepath.Join(gitPrj, filename), "checkout", commits[0].SHA))
|
||||||
common.LogDebug(" -> updated to", commits[0].SHA)
|
log.Println(" -> updated to", commits[0].SHA)
|
||||||
isGitUpdated = true
|
isGitUpdated = true
|
||||||
} else {
|
} else {
|
||||||
// probably need `merge-base` or `rev-list` here instead, or the project updated already
|
// probably need `merge-base` or `rev-list` here instead, or the project updated already
|
||||||
common.LogInfo(" *** Cannot find SHA of last matching update for package:", filename, " Ignoring")
|
log.Println(" *** Cannot find SHA of last matching update for package:", filename, " Ignoring")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find all missing repositories, and add them
|
// find all missing repositories, and add them
|
||||||
common.LogDebug("checking for missing repositories...")
|
if DebugMode {
|
||||||
|
log.Println("checking for missing repositories...")
|
||||||
|
}
|
||||||
repos, err := gitea.GetOrganizationRepositories(org)
|
repos, err := gitea.GetOrganizationRepositories(org)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogDebug(" nRepos:", len(repos))
|
if DebugMode {
|
||||||
|
log.Println(" nRepos:", len(repos))
|
||||||
|
}
|
||||||
|
|
||||||
/* Check repositories in org to make sure they are included in project git */
|
/* Check repositories in org to make sure they are included in project git */
|
||||||
next_repo:
|
next_repo:
|
||||||
for _, r := range repos {
|
for _, r := range repos {
|
||||||
|
if DebugMode {
|
||||||
|
log.Println(" -- checking", r.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.ObjectFormatName != "sha256" {
|
||||||
|
if DebugMode {
|
||||||
|
log.Println(" + ", r.ObjectFormatName, ". Needs to be sha256. Ignoring")
|
||||||
|
}
|
||||||
|
continue next_repo
|
||||||
|
}
|
||||||
|
|
||||||
// for _, c := range configs {
|
// for _, c := range configs {
|
||||||
if gitPrj == r.Name {
|
if gitPrj == r.Name {
|
||||||
// ignore project gits
|
// ignore project gits
|
||||||
@@ -402,45 +390,43 @@ next_repo:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogDebug(" -- checking repository:", r.Name)
|
if DebugMode {
|
||||||
|
log.Println(" -- checking repository:", r.Name)
|
||||||
branch := config.Branch
|
|
||||||
if len(branch) == 0 {
|
|
||||||
branch = r.DefaultBranch
|
|
||||||
if common.IsRemovedBranch(branch) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if commits, err := gitea.GetRecentCommits(org, r.Name, branch, 1); err != nil || len(commits) == 0 {
|
|
||||||
|
if _, err := gitea.GetRecentCommits(org, r.Name, config.Branch, 1); err != nil {
|
||||||
// assumption that package does not exist, so not part of project
|
// assumption that package does not exist, so not part of project
|
||||||
// https://github.com/go-gitea/gitea/issues/31976
|
// https://github.com/go-gitea/gitea/issues/31976
|
||||||
|
|
||||||
// or, we do not have commits here
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// add repository to git project
|
// add repository to git project
|
||||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", r.CloneURL, r.Name))
|
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--depth", "1", r.CloneURL, r.Name))
|
||||||
|
|
||||||
curBranch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, r.Name), "branch", "--show-current"))
|
if len(config.Branch) > 0 {
|
||||||
if branch != curBranch {
|
branch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, r.Name), "branch", "--show-current"))
|
||||||
if err := git.GitExec(path.Join(gitPrj, r.Name), "fetch", "--depth", "1", "origin", branch+":"+branch); err != nil {
|
if branch != config.Branch {
|
||||||
return fmt.Errorf("Fetching branch %s for %s/%s failed. Ignoring.", branch, repo.Owner.UserName, r.Name)
|
if err := git.GitExec(path.Join(gitPrj, r.Name), "fetch", "--depth", "1", "origin", config.Branch+":"+config.Branch); err != nil {
|
||||||
|
return fmt.Errorf("Fetching branch %s for %s/%s failed. Ignoring.", config.Branch, repo.Owner.UserName, r.Name)
|
||||||
|
}
|
||||||
|
common.PanicOnError(git.GitExec(path.Join(gitPrj, r.Name), "checkout", config.Branch))
|
||||||
}
|
}
|
||||||
common.PanicOnError(git.GitExec(path.Join(gitPrj, r.Name), "checkout", branch))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isGitUpdated = true
|
isGitUpdated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if isGitUpdated {
|
if isGitUpdated {
|
||||||
common.PanicOnError(git.GitExec(gitPrj, "commit", "-a", "-m", "Periodic SYNC in Direct Workflow"))
|
common.PanicOnError(git.GitExec(gitPrj, "commit", "-a", "-m", "Automatic update via push via Direct Workflow -- SYNC"))
|
||||||
if !noop {
|
if !noop {
|
||||||
git.GitExecOrPanic(gitPrj, "push", remoteName)
|
git.GitExecOrPanic(gitPrj, "push", remoteName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogInfo("Verification finished for ", org, ", prjgit:", config.GitProjectName)
|
if DebugMode {
|
||||||
|
log.Println("Verification finished for ", org, ", prjgit:", config.GitProjectName)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,17 +437,17 @@ var checkInterval time.Duration
|
|||||||
func checkOrg(org string, configs []*common.AutogitConfig) {
|
func checkOrg(org string, configs []*common.AutogitConfig) {
|
||||||
git, err := gh.CreateGitHandler(org)
|
git, err := gh.CreateGitHandler(org)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Failed to allocate GitHandler:", err)
|
log.Println("Faield to allocate GitHandler:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer git.Close()
|
defer git.Close()
|
||||||
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
common.LogInfo(" ++ starting verification, org:", org, "config:", config.GitProjectName)
|
log.Printf(" ++ starting verification, org: `%s` config: `%s`\n", org, config.GitProjectName)
|
||||||
if err := verifyProjectState(git, org, config, configs); err != nil {
|
if err := verifyProjectState(git, org, config, configs); err != nil {
|
||||||
common.LogError(" *** verification failed, org:", org, err)
|
log.Printf(" *** verification failed, org: `%s`, err: %#v\n", org, err)
|
||||||
} else {
|
} else {
|
||||||
common.LogError(" ++ verification complete, org:", org, config.GitProjectName)
|
log.Printf(" ++ verification complete, org: `%s` config: `%s`\n", org, config.GitProjectName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -470,7 +456,7 @@ func checkRepos() {
|
|||||||
for org, configs := range configuredRepos {
|
for org, configs := range configuredRepos {
|
||||||
if checkInterval > 0 {
|
if checkInterval > 0 {
|
||||||
sleepInterval := checkInterval - checkInterval/2 + time.Duration(rand.Int63n(int64(checkInterval)))
|
sleepInterval := checkInterval - checkInterval/2 + time.Duration(rand.Int63n(int64(checkInterval)))
|
||||||
common.LogInfo(" - sleep interval", sleepInterval, "until next check")
|
log.Println(" - sleep interval", sleepInterval, "until next check")
|
||||||
time.Sleep(sleepInterval)
|
time.Sleep(sleepInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,9 +468,9 @@ func consistencyCheckProcess() {
|
|||||||
if checkOnStart {
|
if checkOnStart {
|
||||||
savedCheckInterval := checkInterval
|
savedCheckInterval := checkInterval
|
||||||
checkInterval = 0
|
checkInterval = 0
|
||||||
common.LogInfo("== Startup consistency check begin...")
|
log.Println("== Startup consistency check begin...")
|
||||||
checkRepos()
|
checkRepos()
|
||||||
common.LogInfo("== Startup consistency check done...")
|
log.Println("== Startup consistency check done...")
|
||||||
checkInterval = savedCheckInterval
|
checkInterval = savedCheckInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,8 +485,7 @@ var gh common.GitHandlerGenerator
|
|||||||
func updateConfiguration(configFilename string, orgs *[]string) {
|
func updateConfiguration(configFilename string, orgs *[]string) {
|
||||||
configFile, err := common.ReadConfigFile(configFilename)
|
configFile, err := common.ReadConfigFile(configFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(err)
|
log.Fatal(err)
|
||||||
os.Exit(4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configs, _ := common.ResolveWorkflowConfigs(gitea, configFile)
|
configs, _ := common.ResolveWorkflowConfigs(gitea, configFile)
|
||||||
@@ -508,7 +493,9 @@ func updateConfiguration(configFilename string, orgs *[]string) {
|
|||||||
*orgs = make([]string, 0, 1)
|
*orgs = make([]string, 0, 1)
|
||||||
for _, c := range configs {
|
for _, c := range configs {
|
||||||
if slices.Contains(c.Workflows, "direct") {
|
if slices.Contains(c.Workflows, "direct") {
|
||||||
common.LogDebug(" + adding org:", c.Organization, ", branch:", c.Branch, ", prjgit:", c.GitProjectName)
|
if DebugMode {
|
||||||
|
log.Printf(" + adding org: '%s', branch: '%s', prjgit: '%s'\n", c.Organization, c.Branch, c.GitProjectName)
|
||||||
|
}
|
||||||
configs := configuredRepos[c.Organization]
|
configs := configuredRepos[c.Organization]
|
||||||
if configs == nil {
|
if configs == nil {
|
||||||
configs = make([]*common.AutogitConfig, 0, 1)
|
configs = make([]*common.AutogitConfig, 0, 1)
|
||||||
@@ -522,7 +509,7 @@ func updateConfiguration(configFilename string, orgs *[]string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
configFilename := flag.String("config", "config.json", "List of PrjGit")
|
configFilename := flag.String("config", "", "List of PrjGit")
|
||||||
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance")
|
giteaUrl := flag.String("gitea-url", "https://src.opensuse.org", "Gitea instance")
|
||||||
rabbitUrl := flag.String("url", "amqps://rabbit.opensuse.org", "URL for RabbitMQ instance")
|
rabbitUrl := flag.String("url", "amqps://rabbit.opensuse.org", "URL for RabbitMQ instance")
|
||||||
flag.BoolVar(&DebugMode, "debug", false, "Extra debugging information")
|
flag.BoolVar(&DebugMode, "debug", false, "Extra debugging information")
|
||||||
@@ -533,35 +520,10 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if err := common.RequireGiteaSecretToken(); err != nil {
|
if err := common.RequireGiteaSecretToken(); err != nil {
|
||||||
common.LogError(err)
|
log.Fatal(err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if err := common.RequireRabbitSecrets(); err != nil {
|
if err := common.RequireRabbitSecrets(); err != nil {
|
||||||
common.LogError(err)
|
log.Fatal(err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cf := os.Getenv("AUTOGITS_CONFIG"); len(cf) > 0 {
|
|
||||||
*configFilename = cf
|
|
||||||
}
|
|
||||||
if url := os.Getenv("AUTOGITS_URL"); len(url) > 0 {
|
|
||||||
*giteaUrl = url
|
|
||||||
}
|
|
||||||
if url := os.Getenv("AUTOGITS_RABBITURL"); len(url) > 0 {
|
|
||||||
*rabbitUrl = url
|
|
||||||
}
|
|
||||||
if debug := os.Getenv("AUTOGITS_DEBUG"); len(debug) > 0 {
|
|
||||||
DebugMode = true
|
|
||||||
}
|
|
||||||
if check := os.Getenv("AUTOGITS_CHECK_ON_START"); len(check) > 0 {
|
|
||||||
checkOnStart = true
|
|
||||||
}
|
|
||||||
if p := os.Getenv("AUTOGITS_REPO_PATH"); len(p) > 0 {
|
|
||||||
*basePath = p
|
|
||||||
}
|
|
||||||
|
|
||||||
if DebugMode {
|
|
||||||
common.SetLoggingLevel(common.LogLevelDebug)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defs := &common.RabbitMQGiteaEventsProcessor{}
|
defs := &common.RabbitMQGiteaEventsProcessor{}
|
||||||
@@ -570,14 +532,12 @@ func main() {
|
|||||||
if len(*basePath) == 0 {
|
if len(*basePath) == 0 {
|
||||||
*basePath, err = os.MkdirTemp(os.TempDir(), AppName)
|
*basePath, err = os.MkdirTemp(os.TempDir(), AppName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(err)
|
log.Fatal(err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gh, err = common.AllocateGitWorkTree(*basePath, GitAuthor, GitEmail)
|
gh, err = common.AllocateGitWorkTree(*basePath, GitAuthor, GitEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(err)
|
log.Fatal(err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle reconfiguration
|
// handle reconfiguration
|
||||||
@@ -592,10 +552,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sig != syscall.SIGHUP {
|
if sig != syscall.SIGHUP {
|
||||||
common.LogError("Unexpected signal received:", sig)
|
log.Println("Unexpected signal received:", sig)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
common.LogError("*** Reconfiguring ***")
|
log.Println("*** Reconfiguring ***")
|
||||||
updateConfiguration(*configFilename, &defs.Orgs)
|
updateConfiguration(*configFilename, &defs.Orgs)
|
||||||
defs.Connection().UpdateTopics(defs)
|
defs.Connection().UpdateTopics(defs)
|
||||||
}
|
}
|
||||||
@@ -607,25 +567,23 @@ func main() {
|
|||||||
gitea = common.AllocateGiteaTransport(*giteaUrl)
|
gitea = common.AllocateGiteaTransport(*giteaUrl)
|
||||||
CurrentUser, err := gitea.GetCurrentUser()
|
CurrentUser, err := gitea.GetCurrentUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Cannot fetch current user:", err)
|
log.Fatalln("Cannot fetch current user:", err)
|
||||||
os.Exit(2)
|
|
||||||
}
|
}
|
||||||
common.LogInfo("Current User:", CurrentUser.UserName)
|
log.Println("Current User:", CurrentUser.UserName)
|
||||||
|
|
||||||
updateConfiguration(*configFilename, &defs.Orgs)
|
updateConfiguration(*configFilename, &defs.Orgs)
|
||||||
|
|
||||||
defs.Connection().RabbitURL, err = url.Parse(*rabbitUrl)
|
defs.Connection().RabbitURL, err = url.Parse(*rabbitUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("cannot parse server URL. Err:", err)
|
log.Panicf("cannot parse server URL. Err: %#v\n", err)
|
||||||
os.Exit(3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go consistencyCheckProcess()
|
go consistencyCheckProcess()
|
||||||
common.LogInfo("defs:", *defs)
|
log.Println("defs:", *defs)
|
||||||
|
|
||||||
defs.Handlers = make(map[string]common.RequestProcessor)
|
defs.Handlers = make(map[string]common.RequestProcessor)
|
||||||
defs.Handlers[common.RequestType_Push] = &PushActionProcessor{}
|
defs.Handlers[common.RequestType_Push] = &PushActionProcessor{}
|
||||||
defs.Handlers[common.RequestType_Repository] = &RepositoryActionProcessor{}
|
defs.Handlers[common.RequestType_Repository] = &RepositoryActionProcessor{}
|
||||||
|
|
||||||
common.LogError(common.ProcessRabbitMQEvents(defs))
|
log.Fatal(common.ProcessRabbitMQEvents(defs))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ JSON
|
|||||||
* _ReviewRequired_: (true, false) ignores that submitter is a maintainer and require a review from other maintainer IFF available
|
* _ReviewRequired_: (true, false) ignores that submitter is a maintainer and require a review from other maintainer IFF available
|
||||||
* _NoProjectGitPR_: (true, false) do not create PrjGit PRs, but still process reviews, etc.
|
* _NoProjectGitPR_: (true, false) do not create PrjGit PRs, but still process reviews, etc.
|
||||||
* _Permissions_: permissions and associated accounts/groups. See below.
|
* _Permissions_: permissions and associated accounts/groups. See below.
|
||||||
* _Labels_: (string, string) Labels for PRs. See below.
|
|
||||||
|
|
||||||
NOTE: `-rm`, `-removed`, `-deleted` are all removed suffixes used to indicate current branch is a placeholder for previously existing package. These branches will be ignored by the bot, and if default, the package will be removed and will not be added to the project.
|
NOTE: `-rm`, `-removed`, `-deleted` are all removed suffixes used to indicate current branch is a placeholder for previously existing package. These branches will be ignored by the bot, and if default, the package will be removed and will not be added to the project.
|
||||||
example:
|
example:
|
||||||
@@ -75,18 +74,6 @@ results in
|
|||||||
* moo -> package and project reviews, but ignored
|
* moo -> package and project reviews, but ignored
|
||||||
|
|
||||||
|
|
||||||
Labels
|
|
||||||
------
|
|
||||||
|
|
||||||
The following labels are used, when defined in Repo/Org.
|
|
||||||
|
|
||||||
| Label Config Entry | Default label | Description
|
|
||||||
|--------------------|----------------|----------------------------------------
|
|
||||||
| StagingAuto | staging/Auto | Assigned to Project Git PRs when first staged
|
|
||||||
| ReviewPending | review/Pending | Assigned to PR when reviews are still pending
|
|
||||||
| ReviewDone | review/Done | Assigned to PR when reviews are complete on this particular PR
|
|
||||||
|
|
||||||
|
|
||||||
Maintainership
|
Maintainership
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|||||||
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() error
|
||||||
|
ConsistencyCheckProcess() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type PRToProcess struct {
|
||||||
|
Org, Repo, Branch string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ func main() {
|
|||||||
common.RequestType_PRSync: req,
|
common.RequestType_PRSync: req,
|
||||||
common.RequestType_PRReviewAccepted: req,
|
common.RequestType_PRReviewAccepted: req,
|
||||||
common.RequestType_PRReviewRejected: req,
|
common.RequestType_PRReviewRejected: req,
|
||||||
common.RequestType_PRComment: req,
|
common.RequestType_IssueComment: req,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
listenDefs.Connection().RabbitURL, _ = url.Parse(*rabbitUrl)
|
listenDefs.Connection().RabbitURL, _ = url.Parse(*rabbitUrl)
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -20,6 +22,83 @@ func TestProjectBranchName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LocalCMD = "---"
|
||||||
|
|
||||||
|
func gitExecs(t *testing.T, git *common.GitHandlerImpl, cmds [][]string) {
|
||||||
|
for _, cmd := range cmds {
|
||||||
|
if cmd[0] == LocalCMD {
|
||||||
|
command := exec.Command(cmd[2], cmd[3:]...)
|
||||||
|
command.Dir = filepath.Join(git.GitPath, cmd[1])
|
||||||
|
command.Stdin = nil
|
||||||
|
command.Env = append([]string{"GIT_CONFIG_COUNT=1", "GIT_CONFIG_KEY_1=protocol.file.allow", "GIT_CONFIG_VALUE_1=always"}, common.ExtraGitParams...)
|
||||||
|
_, err := command.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(" *** error: %v\n", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
git.GitExecOrPanic(cmd[0], cmd[1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandsForPackages(dir, prefix string, startN, endN int) [][]string {
|
||||||
|
commands := make([][]string, (endN-startN+2)*6)
|
||||||
|
|
||||||
|
if dir == "" {
|
||||||
|
dir = "."
|
||||||
|
}
|
||||||
|
cmdIdx := 0
|
||||||
|
for idx := startN; idx <= endN; idx++ {
|
||||||
|
pkgDir := fmt.Sprintf("%s%d", prefix, idx)
|
||||||
|
|
||||||
|
commands[cmdIdx+0] = []string{"", "init", "-q", "--object-format", "sha256", "-b", "testing", pkgDir}
|
||||||
|
commands[cmdIdx+1] = []string{LocalCMD, pkgDir, "/usr/bin/touch", "testFile"}
|
||||||
|
commands[cmdIdx+2] = []string{pkgDir, "add", "testFile"}
|
||||||
|
commands[cmdIdx+3] = []string{pkgDir, "commit", "-m", "added testFile"}
|
||||||
|
commands[cmdIdx+4] = []string{pkgDir, "config", "receive.denyCurrentBranch", "ignore"}
|
||||||
|
commands[cmdIdx+5] = []string{"prj", "submodule", "add", filepath.Join("..", pkgDir), filepath.Join(dir, pkgDir)}
|
||||||
|
|
||||||
|
cmdIdx += 6
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all the submodules to the prj
|
||||||
|
commands[cmdIdx+0] = []string{"prj", "commit", "-a", "-m", "adding subpackages"}
|
||||||
|
|
||||||
|
return commands
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupGitForTests(t *testing.T, git *common.GitHandlerImpl) {
|
||||||
|
common.ExtraGitParams = []string{
|
||||||
|
"GIT_CONFIG_COUNT=1",
|
||||||
|
"GIT_CONFIG_KEY_0=protocol.file.allow",
|
||||||
|
"GIT_CONFIG_VALUE_0=always",
|
||||||
|
|
||||||
|
"GIT_AUTHOR_NAME=testname",
|
||||||
|
"GIT_AUTHOR_EMAIL=test@suse.com",
|
||||||
|
"GIT_AUTHOR_DATE='2005-04-07T22:13:13'",
|
||||||
|
"GIT_COMMITTER_NAME=testname",
|
||||||
|
"GIT_COMMITTER_EMAIL=test@suse.com",
|
||||||
|
"GIT_COMMITTER_DATE='2005-04-07T22:13:13'",
|
||||||
|
}
|
||||||
|
|
||||||
|
gitExecs(t, git, [][]string{
|
||||||
|
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "prj"},
|
||||||
|
{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "foo"},
|
||||||
|
{LocalCMD, "foo", "/usr/bin/touch", "file1"},
|
||||||
|
{"foo", "add", "file1"},
|
||||||
|
{"foo", "commit", "-m", "first commit"},
|
||||||
|
{"prj", "config", "receive.denyCurrentBranch", "ignore"},
|
||||||
|
{"prj", "submodule", "init"},
|
||||||
|
{"prj", "submodule", "add", "../foo", "testRepo"},
|
||||||
|
{"prj", "add", ".gitmodules", "testRepo"},
|
||||||
|
{"prj", "commit", "-m", "First instance"},
|
||||||
|
{"prj", "submodule", "deinit", "testRepo"},
|
||||||
|
{LocalCMD, "foo", "/usr/bin/touch", "file2"},
|
||||||
|
{"foo", "add", "file2"},
|
||||||
|
{"foo", "commit", "-m", "added file2"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpdatePrBranch(t *testing.T) {
|
func TestUpdatePrBranch(t *testing.T) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
origLogger := log.Writer()
|
origLogger := log.Writer()
|
||||||
@@ -46,7 +125,7 @@ func TestUpdatePrBranch(t *testing.T) {
|
|||||||
req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
|
req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
|
||||||
req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0])
|
req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0])
|
||||||
|
|
||||||
updateSubmoduleInPR("testRepo", revs[0], git)
|
updateSubmoduleInPR("mainRepo", revs[0], git)
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit"))
|
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit"))
|
||||||
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:+testing"))
|
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:+testing"))
|
||||||
git.GitExecOrPanic("prj", "reset", "--hard", "testing")
|
git.GitExecOrPanic("prj", "reset", "--hard", "testing")
|
||||||
|
|||||||
10
workflow-pr/mock/pr_processor.go
Normal file
10
workflow-pr/mock/pr_processor.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: pr_processor.go
|
||||||
|
//
|
||||||
|
// Generated by this command:
|
||||||
|
//
|
||||||
|
// mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
|
||||||
|
//
|
||||||
|
|
||||||
|
// Package mock_main is a generated GoMock package.
|
||||||
|
package mock_main
|
||||||
@@ -3,18 +3,18 @@
|
|||||||
//
|
//
|
||||||
// Generated by this command:
|
// Generated by this command:
|
||||||
//
|
//
|
||||||
// mockgen -source=state_checker.go -destination=mock_state_checker.go -typed -package main
|
// mockgen -source=state_checker.go -destination=../mock/state_checker.go -typed -package mock_main
|
||||||
//
|
//
|
||||||
|
|
||||||
// Package main is a generated GoMock package.
|
// Package mock_main is a generated GoMock package.
|
||||||
package main
|
package mock_main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
gomock "go.uber.org/mock/gomock"
|
gomock "go.uber.org/mock/gomock"
|
||||||
common "src.opensuse.org/autogits/common"
|
common "src.opensuse.org/autogits/common"
|
||||||
models "src.opensuse.org/autogits/common/gitea-generated/models"
|
interfaces "src.opensuse.org/autogits/workflow-pr/interfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockStateChecker is a mock of StateChecker interface.
|
// MockStateChecker is a mock of StateChecker interface.
|
||||||
@@ -42,9 +42,11 @@ func (m *MockStateChecker) EXPECT() *MockStateCheckerMockRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckRepos mocks base method.
|
// CheckRepos mocks base method.
|
||||||
func (m *MockStateChecker) CheckRepos() {
|
func (m *MockStateChecker) CheckRepos() error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
m.ctrl.Call(m, "CheckRepos")
|
ret := m.ctrl.Call(m, "CheckRepos")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckRepos indicates an expected call of CheckRepos.
|
// CheckRepos indicates an expected call of CheckRepos.
|
||||||
@@ -60,19 +62,19 @@ type MockStateCheckerCheckReposCall struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockStateCheckerCheckReposCall) Return() *MockStateCheckerCheckReposCall {
|
func (c *MockStateCheckerCheckReposCall) Return(arg0 error) *MockStateCheckerCheckReposCall {
|
||||||
c.Call = c.Call.Return()
|
c.Call = c.Call.Return(arg0)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockStateCheckerCheckReposCall) Do(f func()) *MockStateCheckerCheckReposCall {
|
func (c *MockStateCheckerCheckReposCall) Do(f func() error) *MockStateCheckerCheckReposCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockStateCheckerCheckReposCall) DoAndReturn(f func()) *MockStateCheckerCheckReposCall {
|
func (c *MockStateCheckerCheckReposCall) DoAndReturn(f func() error) *MockStateCheckerCheckReposCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -116,10 +118,10 @@ func (c *MockStateCheckerConsistencyCheckProcessCall) DoAndReturn(f func() error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VerifyProjectState mocks base method.
|
// VerifyProjectState mocks base method.
|
||||||
func (m *MockStateChecker) VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error) {
|
func (m *MockStateChecker) VerifyProjectState(configs *common.AutogitConfig) ([]*interfaces.PRToProcess, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "VerifyProjectState", configs)
|
ret := m.ctrl.Call(m, "VerifyProjectState", configs)
|
||||||
ret0, _ := ret[0].([]*PRToProcess)
|
ret0, _ := ret[0].([]*interfaces.PRToProcess)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
@@ -137,81 +139,19 @@ type MockStateCheckerVerifyProjectStateCall struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
// Return rewrite *gomock.Call.Return
|
||||||
func (c *MockStateCheckerVerifyProjectStateCall) Return(arg0 []*PRToProcess, arg1 error) *MockStateCheckerVerifyProjectStateCall {
|
func (c *MockStateCheckerVerifyProjectStateCall) Return(arg0 []*interfaces.PRToProcess, arg1 error) *MockStateCheckerVerifyProjectStateCall {
|
||||||
c.Call = c.Call.Return(arg0, arg1)
|
c.Call = c.Call.Return(arg0, arg1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
// Do rewrite *gomock.Call.Do
|
||||||
func (c *MockStateCheckerVerifyProjectStateCall) Do(f func(*common.AutogitConfig) ([]*PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
func (c *MockStateCheckerVerifyProjectStateCall) Do(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
||||||
c.Call = c.Call.Do(f)
|
c.Call = c.Call.Do(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||||
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
func (c *MockStateCheckerVerifyProjectStateCall) DoAndReturn(f func(*common.AutogitConfig) ([]*interfaces.PRToProcess, error)) *MockStateCheckerVerifyProjectStateCall {
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPullRequestProcessor is a mock of PullRequestProcessor interface.
|
|
||||||
type MockPullRequestProcessor struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockPullRequestProcessorMockRecorder
|
|
||||||
isgomock struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPullRequestProcessorMockRecorder is the mock recorder for MockPullRequestProcessor.
|
|
||||||
type MockPullRequestProcessorMockRecorder struct {
|
|
||||||
mock *MockPullRequestProcessor
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockPullRequestProcessor creates a new mock instance.
|
|
||||||
func NewMockPullRequestProcessor(ctrl *gomock.Controller) *MockPullRequestProcessor {
|
|
||||||
mock := &MockPullRequestProcessor{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockPullRequestProcessorMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockPullRequestProcessor) EXPECT() *MockPullRequestProcessorMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process mocks base method.
|
|
||||||
func (m *MockPullRequestProcessor) Process(req *models.PullRequest) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Process", req)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process indicates an expected call of Process.
|
|
||||||
func (mr *MockPullRequestProcessorMockRecorder) Process(req any) *MockPullRequestProcessorProcessCall {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*MockPullRequestProcessor)(nil).Process), req)
|
|
||||||
return &MockPullRequestProcessorProcessCall{Call: call}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPullRequestProcessorProcessCall wrap *gomock.Call
|
|
||||||
type MockPullRequestProcessorProcessCall struct {
|
|
||||||
*gomock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return rewrite *gomock.Call.Return
|
|
||||||
func (c *MockPullRequestProcessorProcessCall) Return(arg0 error) *MockPullRequestProcessorProcessCall {
|
|
||||||
c.Call = c.Call.Return(arg0)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do rewrite *gomock.Call.Do
|
|
||||||
func (c *MockPullRequestProcessorProcessCall) Do(f func(*models.PullRequest) error) *MockPullRequestProcessorProcessCall {
|
|
||||||
c.Call = c.Call.Do(f)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
|
||||||
func (c *MockPullRequestProcessorProcessCall) DoAndReturn(f func(*models.PullRequest) error) *MockPullRequestProcessorProcessCall {
|
|
||||||
c.Call = c.Call.DoAndReturn(f)
|
c.Call = c.Call.DoAndReturn(f)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
//go:generate mockgen -source=pr_processor.go -destination=mock/pr_processor.go -typed
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -39,9 +41,6 @@ func PrjGitDescription(prset *common.PRSet) (title string, desc string) {
|
|||||||
refs = append(refs, ref)
|
refs = append(refs, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
slices.Sort(title_refs)
|
|
||||||
slices.Sort(refs)
|
|
||||||
|
|
||||||
title = "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"
|
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"
|
||||||
|
|
||||||
@@ -228,18 +227,12 @@ func (pr *PRProcessor) CreatePRjGitPR(prjGitPRbranch string, prset *common.PRSet
|
|||||||
}
|
}
|
||||||
|
|
||||||
title, desc := PrjGitDescription(prset)
|
title, desc := PrjGitDescription(prset)
|
||||||
pr, err, isNew := Gitea.CreatePullRequestIfNotExist(PrjGit, prjGitPRbranch, PrjGitBranch, title, desc)
|
pr, err := Gitea.CreatePullRequestIfNotExist(PrjGit, prjGitPRbranch, PrjGitBranch, title, desc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Error creating PrjGit PR:", err)
|
common.LogError("Error creating PrjGit PR:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
org := PrjGit.Owner.UserName
|
Gitea.UpdatePullRequest(PrjGit.Owner.UserName, PrjGit.Name, pr.Index, &models.EditPullRequestOption{
|
||||||
repo := PrjGit.Name
|
|
||||||
idx := pr.Index
|
|
||||||
if isNew {
|
|
||||||
Gitea.SetLabels(org, repo, idx, []string{prset.Config.Label(common.Label_StagingAuto)})
|
|
||||||
}
|
|
||||||
Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{
|
|
||||||
RemoveDeadline: true,
|
RemoveDeadline: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -276,7 +269,6 @@ func (pr *PRProcessor) RebaseAndSkipSubmoduleCommits(prset *common.PRSet, branch
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updatePrjGitError_requeue error = errors.New("Commits do not match. Requeing after 5 seconds.")
|
var updatePrjGitError_requeue error = errors.New("Commits do not match. Requeing after 5 seconds.")
|
||||||
|
|
||||||
func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
|
func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
|
||||||
_, _, PrjGitBranch := prset.Config.GetPrjGit()
|
_, _, PrjGitBranch := prset.Config.GetPrjGit()
|
||||||
PrjGitPR, err := prset.GetPrjGitPR()
|
PrjGitPR, err := prset.GetPrjGitPR()
|
||||||
@@ -353,20 +345,7 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update PR
|
// update PR
|
||||||
isPrTitleSame := func(CurrentTitle, NewTitle string) bool {
|
if PrjGitPR.PR.Body != PrjGitBody || PrjGitPR.PR.Title != PrjGitTitle {
|
||||||
ctlen := len(CurrentTitle)
|
|
||||||
for _, suffix := range []string{"...", "…"} {
|
|
||||||
slen := len(suffix)
|
|
||||||
if ctlen > 250 && strings.HasSuffix(CurrentTitle, suffix) && len(NewTitle) > ctlen {
|
|
||||||
NewTitle = NewTitle[0:ctlen-slen] + suffix
|
|
||||||
if CurrentTitle == NewTitle {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return CurrentTitle == NewTitle
|
|
||||||
}
|
|
||||||
if PrjGitPR.PR.User.UserName == CurrentUser.UserName && (PrjGitPR.PR.Body != PrjGitBody || !isPrTitleSame(PrjGitPR.PR.Title, PrjGitTitle)) {
|
|
||||||
Gitea.UpdatePullRequest(PrjGit.Owner.UserName, PrjGit.Name, PrjGitPR.PR.Index, &models.EditPullRequestOption{
|
Gitea.UpdatePullRequest(PrjGit.Owner.UserName, PrjGit.Name, PrjGitPR.PR.Index, &models.EditPullRequestOption{
|
||||||
RemoveDeadline: true,
|
RemoveDeadline: true,
|
||||||
Title: PrjGitTitle,
|
Title: PrjGitTitle,
|
||||||
@@ -402,10 +381,6 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
|
|||||||
prjGitPRbranch := prGitBranchNameForPR(prRepo, prNo)
|
prjGitPRbranch := prGitBranchNameForPR(prRepo, prNo)
|
||||||
prjGitPR, err := prset.GetPrjGitPR()
|
prjGitPR, err := prset.GetPrjGitPR()
|
||||||
if err == common.PRSet_PrjGitMissing {
|
if err == common.PRSet_PrjGitMissing {
|
||||||
if req.State != "open" {
|
|
||||||
common.LogDebug("This PR is closed and no ProjectGit PR. Ignoring.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
common.LogDebug("Missing PrjGit. Need to create one under branch", prjGitPRbranch)
|
common.LogDebug("Missing PrjGit. Need to create one under branch", prjGitPRbranch)
|
||||||
|
|
||||||
if err = pr.CreatePRjGitPR(prjGitPRbranch, prset); err != nil {
|
if err = pr.CreatePRjGitPR(prjGitPRbranch, prset); err != nil {
|
||||||
@@ -552,14 +527,6 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// update prset if we should build it or not
|
|
||||||
if prjGitPR != nil {
|
|
||||||
if file, err := git.GitCatFile(common.DefaultGitPrj, prjGitPR.PR.Head.Sha, "staging.config"); err == nil {
|
|
||||||
prset.HasAutoStaging = (file != nil)
|
|
||||||
common.LogDebug(" -> automatic staging enabled?:", prset.HasAutoStaging)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle case where PrjGit PR is only one left and there are no changes, then we can just close the PR
|
// handle case where PrjGit PR is only one left and there are no changes, then we can just close the PR
|
||||||
if len(prset.PRs) == 1 && prjGitPR != nil && prset.PRs[0] == prjGitPR && prjGitPR.PR.User.UserName == prset.BotUser {
|
if len(prset.PRs) == 1 && prjGitPR != nil && prset.PRs[0] == prjGitPR && prjGitPR.PR.User.UserName == prset.BotUser {
|
||||||
common.LogDebug(" --> checking if superflous PR")
|
common.LogDebug(" --> checking if superflous PR")
|
||||||
@@ -598,14 +565,14 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
|
|||||||
common.LogError("merge error:", err)
|
common.LogError("merge error:", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = prset.AssignReviewers(Gitea, maintainers)
|
prset.AssignReviewers(Gitea, maintainers)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type RequestProcessor struct {
|
type RequestProcessor struct {
|
||||||
configuredRepos map[string][]*common.AutogitConfig
|
configuredRepos map[string][]*common.AutogitConfig
|
||||||
recursive int
|
recursive int
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig) error {
|
func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig) error {
|
||||||
@@ -624,15 +591,6 @@ func ProcesPullRequest(pr *models.PullRequest, configs []*common.AutogitConfig)
|
|||||||
return PRProcessor.Process(pr)
|
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) {
|
func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -655,7 +613,7 @@ func (w *RequestProcessor) ProcessFunc(request *common.Request) (err error) {
|
|||||||
common.LogError("Cannot find PR for issue:", req.Pull_Request.Base.Repo.Owner.Username, req.Pull_Request.Base.Repo.Name, req.Pull_Request.Number)
|
common.LogError("Cannot find PR for issue:", req.Pull_Request.Base.Repo.Owner.Username, req.Pull_Request.Base.Repo.Name, req.Pull_Request.Number)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if req, ok := request.Data.(*common.IssueCommentWebhookEvent); ok {
|
} else if req, ok := request.Data.(*common.IssueWebhookEvent); ok {
|
||||||
pr, err = Gitea.GetPullRequest(req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
|
pr, err = Gitea.GetPullRequest(req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Cannot find PR for issue:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
|
common.LogError("Cannot find PR for issue:", req.Repository.Owner.Username, req.Repository.Name, int64(req.Issue.Number))
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
|
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
mock_common "src.opensuse.org/autogits/common/mock"
|
||||||
)
|
)
|
||||||
@@ -16,7 +17,7 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Reviewers: []string{"reviewer1", "reviewer2"},
|
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||||
Branch: "branch",
|
Branch: "branch",
|
||||||
Organization: "test",
|
Organization: "test",
|
||||||
GitProjectName: "prj#testing",
|
GitProjectName: "prj",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +26,6 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Number: 1,
|
Number: 1,
|
||||||
Pull_Request: &common.PullRequest{
|
Pull_Request: &common.PullRequest{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Number: 1,
|
|
||||||
Base: common.Head{
|
Base: common.Head{
|
||||||
Ref: "branch",
|
Ref: "branch",
|
||||||
Sha: "testing",
|
Sha: "testing",
|
||||||
@@ -53,56 +53,6 @@ func TestOpenPR(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
modelPR := &models.PullRequest{
|
|
||||||
ID: 1,
|
|
||||||
Index: 1,
|
|
||||||
State: "open",
|
|
||||||
User: &models.User{UserName: "testuser"},
|
|
||||||
RequestedReviewers: []*models.User{},
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "branch",
|
|
||||||
Sha: "testing",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "testRepo",
|
|
||||||
Owner: &models.User{
|
|
||||||
UserName: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Ref: "branch",
|
|
||||||
Sha: "testing",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "testRepo",
|
|
||||||
Owner: &models.User{
|
|
||||||
UserName: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockCreatePR := &models.PullRequest{
|
|
||||||
ID: 2,
|
|
||||||
Index: 2,
|
|
||||||
Body: "Forwarded PRs: testRepo\n\nPR: test/testRepo!1",
|
|
||||||
User: &models.User{UserName: "testuser"},
|
|
||||||
RequestedReviewers: []*models.User{},
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "testing",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "prjcopy",
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Sha: "head",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentUser = &models.User{
|
|
||||||
UserName: "testuser",
|
|
||||||
}
|
|
||||||
|
|
||||||
git := &common.GitHandlerImpl{
|
git := &common.GitHandlerImpl{
|
||||||
GitCommiter: "tester",
|
GitCommiter: "tester",
|
||||||
GitEmail: "test@suse.com",
|
GitEmail: "test@suse.com",
|
||||||
@@ -110,46 +60,14 @@ func TestOpenPR(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("PR git opened request against PrjGit == no action", func(t *testing.T) {
|
t.Run("PR git opened request against PrjGit == no action", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
Gitea = mock_common.NewMockGitea(ctl)
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
pr.config.GitProjectName = "testRepo#testing"
|
pr.config.GitProjectName = "testRepo"
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
pr.git = mockGit
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
if err := pr.Process(event); err != nil {
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
|
|
||||||
if err := pr.Process(modelPR); err != nil {
|
|
||||||
t.Error("Error PrjGit opened request. Should be no error.", err)
|
t.Error("Error PrjGit opened request. Should be no error.", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -161,47 +79,39 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Gitea = gitea
|
Gitea = gitea
|
||||||
|
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
pr.config.GitProjectName = "prjcopy#testing"
|
pr.config.GitProjectName = "prjcopy"
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
prjgit := &models.Repository{
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
SSHURL: "./prj",
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
DefaultBranch: "testing",
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
}
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
giteaPR := &models.PullRequest{
|
||||||
|
Base: &models.PRBranchInfo{
|
||||||
|
Repo: &models.Repository{
|
||||||
|
Owner: &models.User{
|
||||||
|
UserName: "test",
|
||||||
|
},
|
||||||
|
Name: "testRepo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
User: &models.User{
|
||||||
|
UserName: "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// gitea.EXPECT().GetAssociatedPrjGitPR("test", "prjcopy", "test", "testRepo", int64(1)).Return(nil, nil)
|
||||||
|
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
|
||||||
|
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(giteaPR, nil)
|
||||||
|
gitea.EXPECT().GetPullRequest("test", "testRepo", int64(1)).Return(giteaPR, nil)
|
||||||
|
gitea.EXPECT().RequestReviews(giteaPR, "reviewer1", "reviewer2").Return(nil, nil)
|
||||||
|
gitea.EXPECT().GetPullRequestReviews("test", "testRepo", int64(0)).Return([]*models.PullReview{}, nil)
|
||||||
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
err := pr.Process(event)
|
||||||
pr.git = mockGit
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := pr.Process(modelPR)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("error:", err)
|
t.Error("error:", err)
|
||||||
}
|
}
|
||||||
@@ -214,44 +124,15 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Gitea = gitea
|
Gitea = gitea
|
||||||
|
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
pr.config.GitProjectName = "prjcopy#testing"
|
pr.config.GitProjectName = "prjcopy"
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
failedErr := errors.New("Returned error here")
|
failedErr := errors.New("Returned error here")
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr).AnyTimes()
|
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(nil, failedErr)
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
err := pr.Process(event)
|
||||||
pr.git = mockGit
|
if err != failedErr {
|
||||||
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := pr.Process(modelPR)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("error:", err)
|
t.Error("error:", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -262,7 +143,7 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Gitea = gitea
|
Gitea = gitea
|
||||||
|
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
pr.config.GitProjectName = "prjcopy#testing"
|
pr.config.GitProjectName = "prjcopy"
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
@@ -271,37 +152,10 @@ func TestOpenPR(t *testing.T) {
|
|||||||
DefaultBranch: "testing",
|
DefaultBranch: "testing",
|
||||||
}
|
}
|
||||||
failedErr := errors.New("Returned error here")
|
failedErr := errors.New("Returned error here")
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjgit, nil).AnyTimes()
|
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr, false)
|
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, failedErr)
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
err := pr.Process(event)
|
||||||
pr.git = mockGit
|
|
||||||
|
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := pr.Process(modelPR)
|
|
||||||
if err != failedErr {
|
if err != failedErr {
|
||||||
t.Error("error:", err)
|
t.Error("error:", err)
|
||||||
}
|
}
|
||||||
@@ -313,49 +167,40 @@ func TestOpenPR(t *testing.T) {
|
|||||||
Gitea = gitea
|
Gitea = gitea
|
||||||
|
|
||||||
event.Repository.Name = "testRepo"
|
event.Repository.Name = "testRepo"
|
||||||
pr.config.GitProjectName = "prjcopy#testing"
|
pr.config.GitProjectName = "prjcopy"
|
||||||
git.GitPath = t.TempDir()
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
|
prjgit := &models.Repository{
|
||||||
|
Name: "SomeRepo",
|
||||||
|
Owner: &models.User{
|
||||||
|
UserName: "org",
|
||||||
|
},
|
||||||
|
SSHURL: "./prj",
|
||||||
|
DefaultBranch: "testing",
|
||||||
|
}
|
||||||
|
giteaPR := &models.PullRequest{
|
||||||
|
Base: &models.PRBranchInfo{
|
||||||
|
Repo: prjgit,
|
||||||
|
},
|
||||||
|
Index: 13,
|
||||||
|
User: &models.User{
|
||||||
|
UserName: "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
failedErr := errors.New("Returned error here")
|
failedErr := errors.New("Returned error here")
|
||||||
|
// gitea.EXPECT().GetAssociatedPrjGitPR("test", "prjcopy", "test", "testRepo", int64(1)).Return(nil, nil)
|
||||||
|
gitea.EXPECT().CreateRepositoryIfNotExist(git, "test", "prjcopy").Return(prjgit, nil)
|
||||||
|
gitea.EXPECT().GetPullRequest("test", "testRepo", int64(1)).Return(giteaPR, nil)
|
||||||
|
gitea.EXPECT().GetPullRequestReviews("org", "SomeRepo", int64(13)).Return([]*models.PullReview{}, nil)
|
||||||
|
gitea.EXPECT().CreatePullRequestIfNotExist(prjgit, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(giteaPR, nil)
|
||||||
|
gitea.EXPECT().RequestReviews(giteaPR, "reviewer1", "reviewer2").Return(nil, failedErr)
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
gitea.EXPECT().FetchMaintainershipDirFile("test", "prjcopy", "branch", "_project").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
gitea.EXPECT().FetchMaintainershipFile("test", "prjcopy", "branch").Return(nil, "", repository.NewRepoGetRawFileNotFound())
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockCreatePR, nil, true).AnyTimes()
|
|
||||||
gitea.EXPECT().RequestReviews(gomock.Any(), gomock.Any()).Return(nil, failedErr).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
err := pr.Process(event)
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
if errors.Unwrap(err) != failedErr {
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
pr.git = mockGit
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"testRepo": "testing"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitCatFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(modelPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetLabels(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Label{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "git@src.opensuse.org:test/prj.git"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
|
|
||||||
Owner: &models.User{UserName: "test"},
|
|
||||||
Name: "prjcopy",
|
|
||||||
SSHURL: "git@src.opensuse.org:test/prj.git",
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := pr.Process(modelPR)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("error:", err)
|
t.Error("error:", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
/*
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
@@ -11,144 +16,217 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestSyncPR(t *testing.T) {
|
func TestSyncPR(t *testing.T) {
|
||||||
config := &common.AutogitConfig{
|
pr := PRProcessor{
|
||||||
Reviewers: []string{"reviewer1", "reviewer2"},
|
config: &common.AutogitConfig{
|
||||||
Branch: "testing",
|
Reviewers: []string{"reviewer1", "reviewer2"},
|
||||||
Organization: "test-org",
|
Branch: "testing",
|
||||||
GitProjectName: "test-prj#testing",
|
Organization: "test",
|
||||||
|
GitProjectName: "prj",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
git := &common.GitHandlerImpl{
|
event := &common.PullRequestWebhookEvent{
|
||||||
GitCommiter: "tester",
|
Action: "syncronized",
|
||||||
GitEmail: "test@suse.com",
|
Number: 42,
|
||||||
GitPath: t.TempDir(),
|
Pull_Request: &common.PullRequest{
|
||||||
}
|
Number: 42,
|
||||||
|
Base: common.Head{
|
||||||
processor := &PRProcessor{
|
Ref: "branch",
|
||||||
config: config,
|
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
|
||||||
git: git,
|
Repo: &common.Repository{
|
||||||
|
Name: "testRepo",
|
||||||
|
Owner: &common.Organization{
|
||||||
|
Username: pr.config.Organization,
|
||||||
|
},
|
||||||
|
Default_Branch: "main1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Head: common.Head{
|
||||||
|
Ref: "branch",
|
||||||
|
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
|
||||||
|
Repo: &common.Repository{
|
||||||
|
Name: "testRepo",
|
||||||
|
Default_Branch: "main1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Repository: &common.Repository{
|
||||||
|
Owner: &common.Organization{
|
||||||
|
Username: pr.config.Organization,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
modelPR := &models.PullRequest{
|
modelPR := &models.PullRequest{
|
||||||
Index: 42,
|
Index: 42,
|
||||||
Body: "PR: test-org/test-prj#24",
|
Body: "PR: test/prj#24",
|
||||||
Base: &models.PRBranchInfo{
|
Base: &models.PRBranchInfo{
|
||||||
Ref: "main",
|
Ref: "branch",
|
||||||
|
Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
Name: "test-repo",
|
Name: "testRepo",
|
||||||
Owner: &models.User{UserName: "test-org"},
|
Owner: &models.User{
|
||||||
DefaultBranch: "main",
|
UserName: "test",
|
||||||
|
},
|
||||||
|
DefaultBranch: "main1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Head: &models.PRBranchInfo{
|
Head: &models.PRBranchInfo{
|
||||||
Ref: "branch",
|
Ref: "branch",
|
||||||
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
|
Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
Name: "test-repo",
|
Name: "testRepo",
|
||||||
Owner: &models.User{UserName: "test-org"},
|
Owner: &models.User{
|
||||||
DefaultBranch: "main",
|
UserName: "test",
|
||||||
|
},
|
||||||
|
DefaultBranch: "main1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
PrjGitPR := &models.PullRequest{
|
PrjGitPR := &models.PullRequest{
|
||||||
Title: "some pull request",
|
Title: "some pull request",
|
||||||
Body: "PR: test-org/test-repo#42",
|
Body: "PR: test/testRepo#42",
|
||||||
Index: 24,
|
Index: 24,
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "testing",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
SSHURL: "url",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
Head: &models.PRBranchInfo{
|
||||||
Name: "PR_test-repo#42",
|
Name: "testing",
|
||||||
Sha: "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95",
|
Sha: "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95",
|
||||||
Repo: &models.Repository{
|
Repo: &models.Repository{
|
||||||
SSHURL: "url",
|
SSHURL: "./prj",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("PR_sync_request_against_PrjGit_==_no_action", func(t *testing.T) {
|
git := &common.GitHandlerImpl{
|
||||||
|
GitCommiter: "tester",
|
||||||
|
GitEmail: "test@suse.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("PR sync request against PrjGit == no action", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
defer ctl.Finish()
|
Gitea = mock_common.NewMockGitea(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
// Common expectations for FetchPRSet and downstream checks
|
git.GitPath = t.TempDir()
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
prjGitRepoPR := &models.PullRequest{
|
pr.config.GitProjectName = "testRepo"
|
||||||
Index: 100,
|
event.Repository.Name = "testRepo"
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "testing",
|
if err := pr.Process(event); err != nil {
|
||||||
Repo: &models.Repository{
|
t.Error("Error PrjGit sync request. Should be no error.", err)
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Ref: "branch",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjGitRepoPR, nil).AnyTimes()
|
t.Run("Missing submodule in prjgit", func(t *testing.T) {
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
ctl := gomock.NewController(t)
|
||||||
|
mock := mock_common.NewMockGitea(ctl)
|
||||||
|
|
||||||
if err := processor.Process(prjGitRepoPR); err != nil {
|
pr.gitea = mock
|
||||||
t.Errorf("Expected nil error for PrjGit sync request, got %v", err)
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
|
pr.config.GitProjectName = "prjGit"
|
||||||
|
event.Repository.Name = "testRepo"
|
||||||
|
|
||||||
|
setupGitForTests(t, git)
|
||||||
|
|
||||||
|
oldSha := PrjGitPR.Head.Sha
|
||||||
|
defer func() { PrjGitPR.Head.Sha = oldSha }()
|
||||||
|
PrjGitPR.Head.Sha = "ab8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95"
|
||||||
|
|
||||||
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
|
||||||
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
|
||||||
|
|
||||||
|
err := pr.Process(event)
|
||||||
|
|
||||||
|
if err == nil || err.Error() != "Cannot fetch submodule commit id in prjgit for 'testRepo'" {
|
||||||
|
t.Error("Invalid error received.", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Missing PrjGit PR for the sync", func(t *testing.T) {
|
t.Run("Missing PrjGit PR for the sync", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
defer ctl.Finish()
|
mock := mock_common.NewMockGitea(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
pr.gitea = mock
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
git.GitPath = t.TempDir()
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
pr.config.GitProjectName = "prjGit"
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("not found")).AnyTimes()
|
event.Repository.Name = "tester"
|
||||||
|
|
||||||
err := processor.Process(modelPR)
|
setupGitForTests(t, git)
|
||||||
// It should fail because it can't find the project PR linked in body
|
|
||||||
if err == nil {
|
expectedErr := errors.New("Missing PR should throw error")
|
||||||
t.Errorf("Expected error for missing project PR, got nil")
|
mock.EXPECT().GetPullRequest(config.Organization, "tester", event.Pull_Request.Number).Return(modelPR, expectedErr)
|
||||||
|
|
||||||
|
err := pr.Process(event, git, config)
|
||||||
|
|
||||||
|
if err == nil || errors.Unwrap(err) != expectedErr {
|
||||||
|
t.Error("Invalid error received.", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("PR sync", func(t *testing.T) {
|
t.Run("PR sync", func(t *testing.T) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
w := log.Writer()
|
||||||
|
log.SetOutput(&b)
|
||||||
|
defer log.SetOutput(w)
|
||||||
|
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
defer ctl.Finish()
|
mock := mock_common.NewMockGitea(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
Gitea = mock
|
||||||
|
git.GitPath = t.TempDir()
|
||||||
|
|
||||||
|
pr.config.GitProjectName = "prjGit"
|
||||||
|
event.Repository.Name = "testRepo"
|
||||||
|
|
||||||
setupGitForTests(t, git)
|
setupGitForTests(t, git)
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(PrjGitPR, nil).AnyTimes()
|
// mock.EXPECT().GetAssociatedPrjGitPR(event).Return(PrjGitPR, nil)
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
// For UpdatePrjGitPR
|
err := pr.Process(event)
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := processor.Process(modelPR)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Error("Invalid error received.", err)
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that we actually created the branch in the prjgit
|
||||||
|
id, ok := git.GitSubmoduleCommitId("prj", "testRepo", "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c")
|
||||||
|
if id != "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985" || !ok {
|
||||||
|
t.Error("Failed creating PR")
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// does nothing on next sync of already synced data -- PR is updated
|
||||||
|
os.RemoveAll(path.Join(git.GitPath, common.DefaultGitPrj))
|
||||||
|
|
||||||
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
|
||||||
|
mock.EXPECT().GetPullRequest(pr.config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
|
||||||
|
err = pr.Process(event)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Invalid error received.", err)
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that we actually created the branch in the prjgit
|
||||||
|
id, ok = git.GitSubmoduleCommitId("prj", "testRepo", "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c")
|
||||||
|
if id != "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985" || !ok {
|
||||||
|
t.Error("Failed creating PR")
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if id, err := git.GitBranchHead("prj", "PR_testRepo#42"); id != "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c" || err != nil {
|
||||||
|
t.Error("no branch?", err)
|
||||||
|
t.Error(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(b.String(), "commitID already match - nothing to do") {
|
||||||
|
// os.CopyFS("/tmp/test", os.DirFS(git.GitPath))
|
||||||
|
t.Log(b.String())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -1,910 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPrjGitDescription(t *testing.T) {
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-a",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 2,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-b",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
GitAuthor = "Bot"
|
|
||||||
title, desc := PrjGitDescription(prset)
|
|
||||||
|
|
||||||
expectedTitle := "Forwarded PRs: pkg-a, pkg-b"
|
|
||||||
if title != expectedTitle {
|
|
||||||
t.Errorf("Expected title %q, got %q", expectedTitle, title)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(desc, "PR: test-org/pkg-a!1") || !strings.Contains(desc, "PR: test-org/pkg-b!2") {
|
|
||||||
t.Errorf("Description missing PR references: %s", desc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAllocatePRProcessor(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
|
|
||||||
configs := common.AutogitConfigs{
|
|
||||||
{
|
|
||||||
Organization: "test-org",
|
|
||||||
Branch: "main",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp/test")
|
|
||||||
|
|
||||||
processor, err := AllocatePRProcessor(req, configs)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("AllocatePRProcessor failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if processor.config.Organization != "test-org" {
|
|
||||||
t.Errorf("Expected organization test-org, got %s", processor.config.Organization)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAllocatePRProcessor_Failures(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
configs := common.AutogitConfigs{} // Empty configs
|
|
||||||
|
|
||||||
req := &models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Missing config", func(t *testing.T) {
|
|
||||||
processor, err := AllocatePRProcessor(req, configs)
|
|
||||||
if err == nil || err.Error() != "Cannot find config for PR" {
|
|
||||||
t.Errorf("Expected 'Cannot find config for PR' error, got %v", err)
|
|
||||||
}
|
|
||||||
if processor != nil {
|
|
||||||
t.Error("Expected nil processor")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("GitHandler failure", func(t *testing.T) {
|
|
||||||
validConfigs := common.AutogitConfigs{
|
|
||||||
{
|
|
||||||
Organization: "test-org",
|
|
||||||
Branch: "main",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(nil, errors.New("git error"))
|
|
||||||
|
|
||||||
processor, err := AllocatePRProcessor(req, validConfigs)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Error allocating GitHandler") {
|
|
||||||
t.Errorf("Expected GitHandler error, got %v", err)
|
|
||||||
}
|
|
||||||
if processor != nil {
|
|
||||||
t.Error("Expected nil processor")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSubmodulesToMatchPRSet_Failures(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("GitSubmoduleList failure", func(t *testing.T) {
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(nil, errors.New("list error"))
|
|
||||||
err := processor.SetSubmodulesToMatchPRSet(&common.PRSet{})
|
|
||||||
if err == nil || err.Error() != "list error" {
|
|
||||||
t.Errorf("Expected 'list error', got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetSubmodulesToMatchPRSet(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-a",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Sha: "new-sha",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "old-sha"}, nil)
|
|
||||||
// Expect submodule update and commit
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitStatus(gomock.Any()).Return([]common.GitStatusData{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := processor.SetSubmodulesToMatchPRSet(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("SetSubmodulesToMatchPRSet failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRebaseAndSkipSubmoduleCommits(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Clean rebase", func(t *testing.T) {
|
|
||||||
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(nil)
|
|
||||||
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Rebase with submodule conflict - skip", func(t *testing.T) {
|
|
||||||
// First rebase fails
|
|
||||||
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(errors.New("conflict"))
|
|
||||||
// Status shows submodule change
|
|
||||||
mockGit.EXPECT().GitStatus(common.DefaultGitPrj).Return([]common.GitStatusData{
|
|
||||||
{SubmoduleChanges: "S..."},
|
|
||||||
}, nil)
|
|
||||||
// Skip called
|
|
||||||
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "--skip").Return(nil)
|
|
||||||
|
|
||||||
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Rebase with real conflict - abort", func(t *testing.T) {
|
|
||||||
mockGit.EXPECT().GitExec(common.DefaultGitPrj, "rebase", "origin/main").Return(errors.New("conflict"))
|
|
||||||
// Status shows real change
|
|
||||||
mockGit.EXPECT().GitStatus(common.DefaultGitPrj).Return([]common.GitStatusData{
|
|
||||||
{SubmoduleChanges: "N..."},
|
|
||||||
}, nil)
|
|
||||||
// Abort called
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(common.DefaultGitPrj, "rebase", "--abort").Return()
|
|
||||||
|
|
||||||
err := processor.RebaseAndSkipSubmoduleCommits(prset, "main")
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Unexpected conflict in rebase") {
|
|
||||||
t.Errorf("Expected 'Unexpected conflict' error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePrjGitPR(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
CurrentUser = &models.User{UserName: "bot"}
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Only project git in PR", func(t *testing.T) {
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Sha: "sha1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(common.DefaultGitPrj, "fetch", "origin", "sha1")
|
|
||||||
err := processor.UpdatePrjGitPR(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("PR on another remote", func(t *testing.T) {
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
RepoID: 1,
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
SSHURL: "url",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Name: "feature",
|
|
||||||
RepoID: 2, // Different RepoID
|
|
||||||
Sha: "sha1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "other",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "other-pkg",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("remote2", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "remote2", "sha1")
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "sha1")
|
|
||||||
|
|
||||||
err := processor.UpdatePrjGitPR(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Standard update with rebase and force push", func(t *testing.T) {
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
BotUser: "bot",
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Mergeable: false, // Triggers rebase
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
RepoID: 1,
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
SSHURL: "url",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Name: "PR_branch",
|
|
||||||
RepoID: 1,
|
|
||||||
Sha: "old-head",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-a",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any())
|
|
||||||
// Rebase expectations
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), "rebase", gomock.Any()).Return(nil)
|
|
||||||
|
|
||||||
// First call returns old-head, second returns new-head to trigger push
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("old-head", nil).Times(1)
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("new-head", nil).Times(1)
|
|
||||||
|
|
||||||
// SetSubmodulesToMatchPRSet expectations
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "old-pkg-sha"}, nil)
|
|
||||||
// Catch all GitExec calls with any number of arguments up to 5
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
// UpdatePullRequest expectation
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := processor.UpdatePrjGitPR(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("isPrTitleSame logic", func(t *testing.T) {
|
|
||||||
longTitle := strings.Repeat("a", 251) + "..."
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
BotUser: "bot",
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
RemoteName: "origin",
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Title: longTitle,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
RepoID: 1,
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-prj",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Name: "PR_branch",
|
|
||||||
RepoID: 1,
|
|
||||||
Sha: "head",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "pkg-a",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any())
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "pkg-sha"}, nil)
|
|
||||||
// mockGit.EXPECT().GitExec(...) not called because no push (headCommit == newHeadCommit)
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
err := processor.UpdatePrjGitPR(prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreatePRjGitPR_Integration(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Repo: &models.Repository{Name: "pkg-a", Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Create new project PR with label", func(t *testing.T) {
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head-sha", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
prjPR := &models.PullRequest{
|
|
||||||
Index: 10,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
RepoID: 1,
|
|
||||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{
|
|
||||||
Sha: "prj-head-sha",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{Owner: &models.User{UserName: "test-org"}}, nil).AnyTimes()
|
|
||||||
// CreatePullRequestIfNotExist returns isNew=true
|
|
||||||
gitea.EXPECT().CreatePullRequestIfNotExist(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil, true).AnyTimes()
|
|
||||||
// Expect SetLabels to be called for new PR
|
|
||||||
gitea.EXPECT().SetLabels("test-org", gomock.Any(), int64(10), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
|
|
||||||
err := processor.CreatePRjGitPR("PR_branch", prset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("CreatePRjGitPR failed: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultiPackagePRSet(t *testing.T) {
|
|
||||||
GitAuthor = "Bot" // Ensure non-empty author
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
name := fmt.Sprintf("pkg-%d", i)
|
|
||||||
prset.PRs = append(prset.PRs, &common.PRInfo{
|
|
||||||
PR: &models.PullRequest{
|
|
||||||
Index: int64(i),
|
|
||||||
State: "open",
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{Name: name, Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
GitAuthor = "Bot"
|
|
||||||
title, desc := PrjGitDescription(prset)
|
|
||||||
|
|
||||||
// PrjGitDescription generates title like "Forwarded PRs: pkg-1, pkg-2, pkg-3, pkg-4, pkg-5"
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
name := fmt.Sprintf("pkg-%d", i)
|
|
||||||
if !strings.Contains(title, name) {
|
|
||||||
t.Errorf("Title missing package %s: %s", name, title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
ref := fmt.Sprintf("PR: test-org/pkg-%d!%d", i, i)
|
|
||||||
if !strings.Contains(desc, ref) {
|
|
||||||
t.Errorf("Description missing reference %s", ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPRProcessor_Process_EdgeCases(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
CurrentUser = &models.User{UserName: "bot"}
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
processor := &PRProcessor{
|
|
||||||
config: config,
|
|
||||||
git: mockGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Merged project PR - update downstream", func(t *testing.T) {
|
|
||||||
prjPR := &models.PullRequest{
|
|
||||||
State: "closed",
|
|
||||||
HasMerged: true,
|
|
||||||
Index: 100,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}, SSHURL: "url"},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Name: "PR_branch"},
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgPR := &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{Name: "main", Repo: &models.Repository{Name: "pkg-a", Owner: &models.User{UserName: "test-org"}}},
|
|
||||||
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
BotUser: "bot",
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{PR: prjPR},
|
|
||||||
{PR: pkgPR},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_ = prset // Suppress unused for now if it's really unused, but it's likely used by common.FetchPRSet internally if we weren't mocking everything
|
|
||||||
|
|
||||||
// Mock expectations for Process setup
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// Mock maintainership file calls
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
// Mock expectations for the merged path
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{"pkg-a": "old-sha"}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{{SHA: "pkg-sha"}}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// Downstream update expectations
|
|
||||||
gitea.EXPECT().AddComment(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
gitea.EXPECT().ManualMergePR(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
err := processor.Process(pkgPR)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Superfluous PR - close it", func(t *testing.T) {
|
|
||||||
prjPR := &models.PullRequest{
|
|
||||||
State: "open",
|
|
||||||
Index: 100,
|
|
||||||
User: &models.User{UserName: "bot"},
|
|
||||||
Body: "Forwarded PRs: \n", // No PRs linked
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Name: "main",
|
|
||||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
Head: &models.PRBranchInfo{Name: "PR_branch", Sha: "head-sha"},
|
|
||||||
MergeBase: "base-sha",
|
|
||||||
}
|
|
||||||
|
|
||||||
prset := &common.PRSet{
|
|
||||||
BotUser: "bot",
|
|
||||||
Config: config,
|
|
||||||
PRs: []*common.PRInfo{
|
|
||||||
{PR: prjPR},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_ = prset
|
|
||||||
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(prjPR, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{Owner: &models.User{UserName: "test-org"}}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// Mock maintainership file calls
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
// Standard update calls within Process
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("head-sha", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
// Diff check for superfluous
|
|
||||||
mockGit.EXPECT().GitDiff(gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil).AnyTimes()
|
|
||||||
|
|
||||||
// Expectations for closing
|
|
||||||
gitea.EXPECT().AddComment(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
gitea.EXPECT().UpdatePullRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
err := processor.Process(prjPR)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVerifyRepositoryConfiguration(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
repo := &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &models.User{
|
|
||||||
UserName: "test-user",
|
|
||||||
},
|
|
||||||
AutodetectManualMerge: true,
|
|
||||||
AllowManualMerge: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Config already correct", func(t *testing.T) {
|
|
||||||
err := verifyRepositoryConfiguration(repo)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Config incorrect - trigger update", func(t *testing.T) {
|
|
||||||
repo.AllowManualMerge = false
|
|
||||||
gitea.EXPECT().SetRepoOptions("test-user", "test-repo", true).Return(&models.Repository{}, nil)
|
|
||||||
|
|
||||||
err := verifyRepositoryConfiguration(repo)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Update failure", func(t *testing.T) {
|
|
||||||
repo.AllowManualMerge = false
|
|
||||||
expectedErr := errors.New("update failed")
|
|
||||||
gitea.EXPECT().SetRepoOptions("test-user", "test-repo", true).Return(nil, expectedErr)
|
|
||||||
|
|
||||||
err := verifyRepositoryConfiguration(repo)
|
|
||||||
if err != expectedErr {
|
|
||||||
t.Errorf("Expected %v, got %v", expectedErr, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProcessFunc(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
reqProc := &RequestProcessor{
|
|
||||||
configuredRepos: map[string][]*common.AutogitConfig{
|
|
||||||
"test-org": {config},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
modelPR := &models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
DefaultBranch: "main",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("PullRequestWebhookEvent", func(t *testing.T) {
|
|
||||||
event := &common.PullRequestWebhookEvent{
|
|
||||||
Pull_Request: &common.PullRequest{
|
|
||||||
Number: 1,
|
|
||||||
Base: common.Head{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &common.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &common.Organization{
|
|
||||||
Username: "test-org",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequest("test-org", "test-repo", int64(1)).Return(modelPR, nil).AnyTimes()
|
|
||||||
// AllocatePRProcessor and ProcesPullRequest calls inside
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
// Expect Process calls (mocked via mockGit mostly)
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := reqProc.ProcessFunc(&common.Request{Data: event})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("IssueCommentWebhookEvent", func(t *testing.T) {
|
|
||||||
event := &common.IssueCommentWebhookEvent{
|
|
||||||
Issue: &common.IssueDetail{Number: 1},
|
|
||||||
Repository: &common.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
Owner: &common.Organization{Username: "test-org"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetPullRequest("test-org", "test-repo", int64(1)).Return(modelPR, nil).AnyTimes()
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
err := reqProc.ProcessFunc(&common.Request{Data: event})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Recursion limit", func(t *testing.T) {
|
|
||||||
reqProc.recursive = 3
|
|
||||||
err := reqProc.ProcessFunc(&common.Request{})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected nil error on recursion limit, got %v", err)
|
|
||||||
}
|
|
||||||
if reqProc.recursive != 3 {
|
|
||||||
t.Errorf("Expected recursive to be 3, got %d", reqProc.recursive)
|
|
||||||
}
|
|
||||||
reqProc.recursive = 0 // Reset
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Invalid data format", func(t *testing.T) {
|
|
||||||
err := reqProc.ProcessFunc(&common.Request{Data: nil})
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Invalid data format") {
|
|
||||||
t.Errorf("Expected 'Invalid data format' error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"path"
|
"path"
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
|
"src.opensuse.org/autogits/workflow-pr/interfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DefaultStateChecker struct {
|
type DefaultStateChecker struct {
|
||||||
@@ -17,11 +19,11 @@ type DefaultStateChecker struct {
|
|||||||
checkOnStart bool
|
checkOnStart bool
|
||||||
checkInterval time.Duration
|
checkInterval time.Duration
|
||||||
|
|
||||||
processor PullRequestProcessor
|
processor *RequestProcessor
|
||||||
i StateChecker
|
i interfaces.StateChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateDefaultStateChecker(checkOnStart bool, processor PullRequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker {
|
func CreateDefaultStateChecker(checkOnStart bool, processor *RequestProcessor, gitea common.Gitea, interval time.Duration) *DefaultStateChecker {
|
||||||
var s = &DefaultStateChecker{
|
var s = &DefaultStateChecker{
|
||||||
checkInterval: interval,
|
checkInterval: interval,
|
||||||
checkOnStart: checkOnStart,
|
checkOnStart: checkOnStart,
|
||||||
@@ -41,19 +43,10 @@ func pullRequestToEventState(state models.StateType) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStateChecker) ProcessPR(pr *models.PullRequest, config *common.AutogitConfig) error {
|
func (s *DefaultStateChecker) ProcessPR(pr *models.PullRequest, config *common.AutogitConfig) error {
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
common.LogError("panic caught in ProcessPR", common.PRtoString(pr))
|
|
||||||
if err, ok := r.(error); !ok {
|
|
||||||
common.LogError(err)
|
|
||||||
}
|
|
||||||
common.LogError(string(debug.Stack()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return ProcesPullRequest(pr, common.AutogitConfigs{config})
|
return ProcesPullRequest(pr, common.AutogitConfigs{config})
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrjGitSubmoduleCheck(config *common.AutogitConfig, git common.Git, repo string, submodules map[string]string) (prsToProcess []*PRToProcess, err error) {
|
func PrjGitSubmoduleCheck(config *common.AutogitConfig, git common.Git, repo string, submodules map[string]string) (prsToProcess []*interfaces.PRToProcess, err error) {
|
||||||
nextSubmodule:
|
nextSubmodule:
|
||||||
for sub, commitID := range submodules {
|
for sub, commitID := range submodules {
|
||||||
common.LogDebug(" + checking", sub, commitID)
|
common.LogDebug(" + checking", sub, commitID)
|
||||||
@@ -73,7 +66,7 @@ nextSubmodule:
|
|||||||
|
|
||||||
branch = repo.DefaultBranch
|
branch = repo.DefaultBranch
|
||||||
}
|
}
|
||||||
prsToProcess = append(prsToProcess, &PRToProcess{
|
prsToProcess = append(prsToProcess, &interfaces.PRToProcess{
|
||||||
Org: config.Organization,
|
Org: config.Organization,
|
||||||
Repo: submoduleName,
|
Repo: submoduleName,
|
||||||
Branch: branch,
|
Branch: branch,
|
||||||
@@ -116,7 +109,7 @@ nextSubmodule:
|
|||||||
return prsToProcess, nil
|
return prsToProcess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) ([]*PRToProcess, error) {
|
func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) ([]*interfaces.PRToProcess, error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
common.LogError("panic caught")
|
common.LogError("panic caught")
|
||||||
@@ -127,7 +120,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
prsToProcess := []*PRToProcess{}
|
prsToProcess := []*interfaces.PRToProcess{}
|
||||||
|
|
||||||
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
|
prjGitOrg, prjGitRepo, prjGitBranch := config.GetPrjGit()
|
||||||
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
|
common.LogInfo(" checking", prjGitOrg+"/"+prjGitRepo+"#"+prjGitBranch)
|
||||||
@@ -147,7 +140,7 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
|
|||||||
_, err = git.GitClone(prjGitRepo, prjGitBranch, repo.SSHURL)
|
_, err = git.GitClone(prjGitRepo, prjGitBranch, repo.SSHURL)
|
||||||
common.PanicOnError(err)
|
common.PanicOnError(err)
|
||||||
|
|
||||||
prsToProcess = append(prsToProcess, &PRToProcess{
|
prsToProcess = append(prsToProcess, &interfaces.PRToProcess{
|
||||||
Org: prjGitOrg,
|
Org: prjGitOrg,
|
||||||
Repo: prjGitRepo,
|
Repo: prjGitRepo,
|
||||||
Branch: prjGitBranch,
|
Branch: prjGitBranch,
|
||||||
@@ -155,11 +148,10 @@ func (s *DefaultStateChecker) VerifyProjectState(config *common.AutogitConfig) (
|
|||||||
submodules, err := git.GitSubmoduleList(prjGitRepo, "HEAD")
|
submodules, err := git.GitSubmoduleList(prjGitRepo, "HEAD")
|
||||||
|
|
||||||
// forward any package-gits referred by the project git, but don't go back
|
// forward any package-gits referred by the project git, but don't go back
|
||||||
subPrs, err := PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules)
|
return PrjGitSubmoduleCheck(config, git, prjGitRepo, submodules)
|
||||||
return append(prsToProcess, subPrs...), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStateChecker) CheckRepos() {
|
func (s *DefaultStateChecker) CheckRepos() error {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
common.LogError("panic caught")
|
common.LogError("panic caught")
|
||||||
@@ -169,9 +161,9 @@ func (s *DefaultStateChecker) CheckRepos() {
|
|||||||
common.LogError(string(debug.Stack()))
|
common.LogError(string(debug.Stack()))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
errorList := make([]error, 0, 10)
|
||||||
|
|
||||||
processor := s.processor.(*RequestProcessor)
|
for org, configs := range s.processor.configuredRepos {
|
||||||
for org, configs := range processor.configuredRepos {
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
if s.checkInterval > 0 {
|
if s.checkInterval > 0 {
|
||||||
sleepInterval := (s.checkInterval - s.checkInterval/2) + time.Duration(rand.Int63n(int64(s.checkInterval)))
|
sleepInterval := (s.checkInterval - s.checkInterval/2) + time.Duration(rand.Int63n(int64(s.checkInterval)))
|
||||||
@@ -183,12 +175,12 @@ func (s *DefaultStateChecker) CheckRepos() {
|
|||||||
prs, err := s.i.VerifyProjectState(config)
|
prs, err := s.i.VerifyProjectState(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(" *** verification failed, org:", org, err)
|
common.LogError(" *** verification failed, org:", org, err)
|
||||||
|
errorList = append(errorList, err)
|
||||||
}
|
}
|
||||||
for _, pr := range prs {
|
for _, pr := range prs {
|
||||||
prs, err := Gitea.GetRecentPullRequests(pr.Org, pr.Repo, pr.Branch)
|
prs, err := Gitea.GetRecentPullRequests(pr.Org, pr.Repo, pr.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError("Error fetching pull requests for", fmt.Sprintf("%s/%s#%s", pr.Org, pr.Repo, pr.Branch), err)
|
return fmt.Errorf("Error fetching pull requests for %s/%s#%s. Err: %w", pr.Org, pr.Repo, pr.Branch, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if len(prs) > 0 {
|
if len(prs) > 0 {
|
||||||
common.LogDebug(fmt.Sprintf("%s/%s#%s", pr.Org, pr.Repo, pr.Branch), " - # of PRs to check:", len(prs))
|
common.LogDebug(fmt.Sprintf("%s/%s#%s", pr.Org, pr.Repo, pr.Branch), " - # of PRs to check:", len(prs))
|
||||||
@@ -201,11 +193,9 @@ func (s *DefaultStateChecker) CheckRepos() {
|
|||||||
|
|
||||||
common.LogInfo(" ++ verification complete, org:", org, "config:", config.GitProjectName)
|
common.LogInfo(" ++ verification complete, org:", org, "config:", config.GitProjectName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(configs) == 0 {
|
|
||||||
common.LogError(" org:", org, "has 0 configs?")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return errors.Join(errorList...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStateChecker) ConsistencyCheckProcess() error {
|
func (s *DefaultStateChecker) ConsistencyCheckProcess() error {
|
||||||
|
|||||||
@@ -1,333 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.uber.org/mock/gomock"
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPrjGitSubmoduleCheck(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
Branch: "main",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Submodule up to date", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{
|
|
||||||
"pkg-a": "sha-1",
|
|
||||||
}
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
|
|
||||||
{SHA: "sha-1"},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
|
|
||||||
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Submodule behind branch", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{
|
|
||||||
"pkg-a": "sha-old",
|
|
||||||
}
|
|
||||||
|
|
||||||
// sha-old is the second commit, meaning it's behind the head (sha-new)
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
|
|
||||||
{SHA: "sha-new"},
|
|
||||||
{SHA: "sha-old"},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
|
|
||||||
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Submodule with new commits - advance branch", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{
|
|
||||||
"pkg-a": "sha-very-new",
|
|
||||||
}
|
|
||||||
|
|
||||||
// sha-very-new is NOT in recent commits
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
|
|
||||||
{SHA: "sha-new"},
|
|
||||||
{SHA: "sha-old"},
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), gomock.Any()).Return("commit-1\n").AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "remote", gomock.Any(), gomock.Any(), gomock.Any()).Return("https://src.opensuse.org/test-org/pkg-a.git\n").AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
|
|
||||||
prs, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("PrjGitSubmoduleCheck failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(prs) != 1 || prs[0].Repo != "pkg-a" {
|
|
||||||
t.Errorf("Expected 1 PR to process for pkg-a, got %v", prs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrjGitSubmoduleCheck_Failures(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
Branch: "main",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("GetRecentCommits failure", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{"pkg-a": "sha-1"}
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return(nil, errors.New("gitea error"))
|
|
||||||
|
|
||||||
_, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Error fetching recent commits") {
|
|
||||||
t.Errorf("Expected gitea error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("SSH translation failure", func(t *testing.T) {
|
|
||||||
submodules := map[string]string{"pkg-a": "sha-new"}
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{{SHA: "sha-old"}}, nil)
|
|
||||||
|
|
||||||
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), gomock.Any()).Return("commit-1\n").AnyTimes()
|
|
||||||
// Return invalid URL that cannot be translated to SSH
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "remote", gomock.Any(), gomock.Any(), gomock.Any()).Return("not-a-url").AnyTimes()
|
|
||||||
|
|
||||||
_, err := PrjGitSubmoduleCheck(config, mockGit, "prj-repo", submodules)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Cannot traslate HTTPS git URL to SSH_URL") {
|
|
||||||
t.Errorf("Expected SSH translation error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPullRequestToEventState(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
state models.StateType
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"open", "opened"},
|
|
||||||
{"closed", "closed"},
|
|
||||||
{"merged", "merged"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
if got := pullRequestToEventState(tt.state); got != tt.expected {
|
|
||||||
t.Errorf("pullRequestToEventState(%v) = %v; want %v", tt.state, got, tt.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultStateChecker_ProcessPR(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
checker := CreateDefaultStateChecker(false, nil, gitea, time.Duration(0))
|
|
||||||
|
|
||||||
pr := &models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{
|
|
||||||
Name: "test-repo",
|
|
||||||
DefaultBranch: "main",
|
|
||||||
Owner: &models.User{UserName: "test-org"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler(gomock.Any()).Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
// Expectations for ProcesPullRequest
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(pr, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
err := checker.ProcessPR(pr, config)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("ProcessPR failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultStateChecker_VerifyProjectState(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
checker := CreateDefaultStateChecker(false, nil, gitea, 0)
|
|
||||||
|
|
||||||
t.Run("VerifyProjectState success", func(t *testing.T) {
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), "test-org", "test-prj").Return(&models.Repository{SSHURL: "url"}, nil)
|
|
||||||
mockGit.EXPECT().GitClone("test-prj", "main", "url").Return("origin", nil)
|
|
||||||
mockGit.EXPECT().GitSubmoduleList("test-prj", "HEAD").Return(map[string]string{"pkg-a": "sha-1"}, nil)
|
|
||||||
|
|
||||||
// PrjGitSubmoduleCheck call inside
|
|
||||||
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{DefaultBranch: "main"}, nil).AnyTimes()
|
|
||||||
// Return commits where sha-1 is NOT present
|
|
||||||
gitea.EXPECT().GetRecentCommits("test-org", "pkg-a", "main", int64(10)).Return([]*models.Commit{
|
|
||||||
{SHA: "sha-new"},
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// rev-list returns empty string, so no new commits on branch relative to submodule commitID
|
|
||||||
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), "rev-list", gomock.Any(), "sha-1").Return("").AnyTimes()
|
|
||||||
// And ensure submodule update is called
|
|
||||||
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "submodule", "update", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return().AnyTimes()
|
|
||||||
|
|
||||||
prs, err := checker.VerifyProjectState(config)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("VerifyProjectState failed: %v", err)
|
|
||||||
}
|
|
||||||
// Expect project git + pkg-a
|
|
||||||
if len(prs) != 2 {
|
|
||||||
t.Errorf("Expected 2 PRs to process, got %d", len(prs))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("VerifyProjectState failure - CreateRepository", func(t *testing.T) {
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil)
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil)
|
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), "test-org", "test-prj").Return(nil, errors.New("gitea error"))
|
|
||||||
|
|
||||||
_, err := checker.VerifyProjectState(config)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Error fetching or creating") {
|
|
||||||
t.Errorf("Expected gitea error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultStateChecker_CheckRepos(t *testing.T) {
|
|
||||||
ctl := gomock.NewController(t)
|
|
||||||
defer ctl.Finish()
|
|
||||||
|
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
|
||||||
Gitea = gitea
|
|
||||||
mockGit := mock_common.NewMockGit(ctl)
|
|
||||||
mockGitGen := mock_common.NewMockGitHandlerGenerator(ctl)
|
|
||||||
GitHandler = mockGitGen
|
|
||||||
|
|
||||||
config := &common.AutogitConfig{
|
|
||||||
Organization: "test-org",
|
|
||||||
GitProjectName: "test-prj#main",
|
|
||||||
}
|
|
||||||
|
|
||||||
reqProc := &RequestProcessor{
|
|
||||||
configuredRepos: map[string][]*common.AutogitConfig{
|
|
||||||
"test-org": {config},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
checker := CreateDefaultStateChecker(false, nil, gitea, 0)
|
|
||||||
checker.processor = reqProc
|
|
||||||
|
|
||||||
t.Run("CheckRepos success with PRs", func(t *testing.T) {
|
|
||||||
// Mock VerifyProjectState results
|
|
||||||
// TODO: fix below
|
|
||||||
// Since we can't easily mock the internal call s.i.VerifyProjectState because s.i is the checker itself
|
|
||||||
// and VerifyProjectState is not a separate interface method in repo_check.go (it is, but used internally).
|
|
||||||
// Actually DefaultStateChecker implements i (StateChecker interface).
|
|
||||||
|
|
||||||
mockGitGen.EXPECT().CreateGitHandler("test-org").Return(mockGit, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
|
|
||||||
mockGit.EXPECT().Close().Return(nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "url"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// GetRecentPullRequests for the project git
|
|
||||||
gitea.EXPECT().GetRecentPullRequests("test-org", "test-prj", "main").Return([]*models.PullRequest{
|
|
||||||
{Index: 1, Base: &models.PRBranchInfo{Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}}}},
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
|
|
||||||
// ProcessPR calls for the found PR
|
|
||||||
gitea.EXPECT().GetPullRequest(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.PullRequest{
|
|
||||||
Index: 1,
|
|
||||||
Base: &models.PRBranchInfo{
|
|
||||||
Ref: "main",
|
|
||||||
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
|
|
||||||
},
|
|
||||||
}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetTimeline(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.TimelineComment{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().GetPullRequestReviews(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullReview{}, nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().FetchMaintainershipDirFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, "", nil).AnyTimes()
|
|
||||||
gitea.EXPECT().SetRepoOptions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
|
|
||||||
|
|
||||||
checker.CheckRepos()
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("CheckRepos failure - GetRecentPullRequests", func(t *testing.T) {
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{SSHURL: "url"}, nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
|
|
||||||
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), gomock.Any()).Return(map[string]string{}, nil).AnyTimes()
|
|
||||||
|
|
||||||
gitea.EXPECT().GetRecentPullRequests("test-org", "test-prj", "main").Return(nil, errors.New("gitea error")).AnyTimes()
|
|
||||||
|
|
||||||
checker.CheckRepos()
|
|
||||||
// Should log error and continue (no panic)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"src.opensuse.org/autogits/common"
|
"src.opensuse.org/autogits/common"
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
||||||
mock_common "src.opensuse.org/autogits/common/mock"
|
mock_common "src.opensuse.org/autogits/common/mock"
|
||||||
|
mock_main "src.opensuse.org/autogits/workflow-pr/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRepoCheck(t *testing.T) {
|
func TestRepoCheck(t *testing.T) {
|
||||||
@@ -21,15 +22,16 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
t.Run("Consistency Check On Start", func(t *testing.T) {
|
t.Run("Consistency Check On Start", func(t *testing.T) {
|
||||||
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
state := NewMockStateChecker(ctl)
|
state := mock_main.NewMockStateChecker(ctl)
|
||||||
c.i = state
|
c.i = state
|
||||||
state.EXPECT().CheckRepos().Do(func() {
|
state.EXPECT().CheckRepos().Do(func() error {
|
||||||
// only checkOnStart has checkInterval = 0
|
// only checkOnStart has checkInterval = 0
|
||||||
if c.checkInterval != 0 {
|
if c.checkInterval != 0 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.exitCheckLoop = true
|
c.exitCheckLoop = true
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
c.ConsistencyCheckProcess()
|
c.ConsistencyCheckProcess()
|
||||||
@@ -41,11 +43,11 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
t.Run("No consistency Check On Start", func(t *testing.T) {
|
t.Run("No consistency Check On Start", func(t *testing.T) {
|
||||||
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
c := CreateDefaultStateChecker(true, nil, nil, 100)
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
state := NewMockStateChecker(ctl)
|
state := mock_main.NewMockStateChecker(ctl)
|
||||||
c.i = state
|
c.i = state
|
||||||
|
|
||||||
nCalls := 10
|
nCalls := 10
|
||||||
state.EXPECT().CheckRepos().Do(func() {
|
state.EXPECT().CheckRepos().Do(func() error {
|
||||||
// only checkOnStart has checkInterval = 0
|
// only checkOnStart has checkInterval = 0
|
||||||
if c.checkInterval != 100 {
|
if c.checkInterval != 100 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@@ -55,6 +57,7 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
if nCalls == 0 {
|
if nCalls == 0 {
|
||||||
c.exitCheckLoop = true
|
c.exitCheckLoop = true
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}).Times(nCalls)
|
}).Times(nCalls)
|
||||||
c.checkOnStart = false
|
c.checkOnStart = false
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("CheckRepos() calls CheckProjectState() for each project", func(t *testing.T) {
|
t.Run("CheckRepos() calls CheckProjectState() for each project", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
state := NewMockStateChecker(ctl)
|
state := mock_main.NewMockStateChecker(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
gitea := mock_common.NewMockGitea(ctl)
|
||||||
|
|
||||||
config1 := &common.AutogitConfig{
|
config1 := &common.AutogitConfig{
|
||||||
@@ -94,12 +97,14 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo2_org"][0])
|
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo2_org"][0])
|
||||||
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo3_org"][0])
|
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo3_org"][0])
|
||||||
|
|
||||||
c.CheckRepos()
|
if err := c.CheckRepos(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("CheckRepos errors", func(t *testing.T) {
|
t.Run("CheckRepos errors", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
state := NewMockStateChecker(ctl)
|
state := mock_main.NewMockStateChecker(ctl)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
gitea := mock_common.NewMockGitea(ctl)
|
||||||
|
|
||||||
config1 := &common.AutogitConfig{
|
config1 := &common.AutogitConfig{
|
||||||
@@ -120,7 +125,11 @@ func TestRepoCheck(t *testing.T) {
|
|||||||
err := errors.New("test error")
|
err := errors.New("test error")
|
||||||
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo1_org"][0]).Return(nil, err)
|
state.EXPECT().VerifyProjectState(configs.configuredRepos["repo1_org"][0]).Return(nil, err)
|
||||||
|
|
||||||
c.CheckRepos()
|
r := c.CheckRepos()
|
||||||
|
|
||||||
|
if !errors.Is(r, err) {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,11 +177,11 @@ func TestVerifyProjectState(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{
|
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), config1.GitProjectName).Return(&models.Repository{
|
||||||
SSHURL: "./prj",
|
SSHURL: "./prj",
|
||||||
}, nil).AnyTimes()
|
}, nil)
|
||||||
gitea.EXPECT().GetRecentPullRequests(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullRequest{}, nil).AnyTimes()
|
gitea.EXPECT().GetRecentPullRequests(org, "testRepo", "testing")
|
||||||
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{}, nil).AnyTimes()
|
gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())
|
||||||
|
|
||||||
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
||||||
/*
|
/*
|
||||||
@@ -190,6 +199,7 @@ func TestVerifyProjectState(t *testing.T) {
|
|||||||
t.Run("Project state with 1 PRs that doesn't trigger updates", func(t *testing.T) {
|
t.Run("Project state with 1 PRs that doesn't trigger updates", func(t *testing.T) {
|
||||||
ctl := gomock.NewController(t)
|
ctl := gomock.NewController(t)
|
||||||
gitea := mock_common.NewMockGitea(ctl)
|
gitea := mock_common.NewMockGitea(ctl)
|
||||||
|
process := mock_main.NewMockPullRequestProcessor(ctl)
|
||||||
|
|
||||||
git := &common.GitHandlerImpl{
|
git := &common.GitHandlerImpl{
|
||||||
GitCommiter: "TestCommiter",
|
GitCommiter: "TestCommiter",
|
||||||
@@ -213,11 +223,11 @@ func TestVerifyProjectState(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.Repository{
|
gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), config1.GitProjectName).Return(&models.Repository{
|
||||||
SSHURL: "./prj",
|
SSHURL: "./prj",
|
||||||
}, nil).AnyTimes()
|
}, nil)
|
||||||
|
|
||||||
gitea.EXPECT().GetRecentPullRequests(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.PullRequest{
|
gitea.EXPECT().GetRecentPullRequests(org, "testRepo", "testing").Return([]*models.PullRequest{
|
||||||
&models.PullRequest{
|
&models.PullRequest{
|
||||||
ID: 1234,
|
ID: 1234,
|
||||||
URL: "url here",
|
URL: "url here",
|
||||||
@@ -249,16 +259,16 @@ func TestVerifyProjectState(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil).AnyTimes()
|
}, nil)
|
||||||
|
|
||||||
gitea.EXPECT().GetRecentCommits(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.Commit{}, nil).AnyTimes()
|
gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())
|
||||||
|
|
||||||
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
c := CreateDefaultStateChecker(false, configs, gitea, 0)
|
||||||
/*
|
/*
|
||||||
c.git = &testGit{
|
c.git = &testGit{
|
||||||
git: git,
|
git: git,
|
||||||
}*/
|
}*/
|
||||||
// process.EXPECT().Process(gomock.Any())
|
process.EXPECT().Process(gomock.Any(), gomock.Any(), gomock.Any())
|
||||||
// c.processor.Opened = process
|
// c.processor.Opened = process
|
||||||
|
|
||||||
_, err := c.VerifyProjectState(configs.configuredRepos[org][0])
|
_, err := c.VerifyProjectState(configs.configuredRepos[org][0])
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"src.opensuse.org/autogits/common"
|
|
||||||
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate mockgen -source=state_checker.go -destination=mock_state_checker.go -typed -package main
|
|
||||||
|
|
||||||
|
|
||||||
type StateChecker interface {
|
|
||||||
VerifyProjectState(configs *common.AutogitConfig) ([]*PRToProcess, error)
|
|
||||||
CheckRepos()
|
|
||||||
ConsistencyCheckProcess() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type PullRequestProcessor interface {
|
|
||||||
Process(req *models.PullRequest) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type PRToProcess struct {
|
|
||||||
Org, Repo, Branch string
|
|
||||||
}
|
|
||||||
@@ -1,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