workflow-pr: rely on the target repository commit instead of the source fork
gofmt / Format checking (pull_request) Successful in 25s
go-generate-check / go-generate-check (pull_request) Successful in 31s
gofmt / Linter and static analysis (pull_request) Failing after 41s
Integration (Selective) / check (pull_request) Successful in 2s
go-test-unit / go-test-unit (pull_request) Successful in 1m7s
Integration (Selective) / integration (pull_request) Failing after 9m36s

Rework of the previous approach following review feedback.

Chosen solution: adapt GitClone instead of adding a parallel helper.
GitClone now checks out a commit given by its object id (SHA) from
FETCH_HEAD, the same way it already handled refs/ references. Every
read-only materialization of a PR head goes through GitClone against the
base repository, reading the head by its pr.Head.Sha, which the bot can
always access even when the source fork is private.

Reverted from the initial implementation:
  - Removed the FetchPRHeadFromTarget helper (interface method, impl and
    regenerated mock) in favour of GitClone.
  - Removed the PullRequestHeadRef builder and switched every fetch from
    refs/pull/<index>/head back to the bare head.Sha (common/pr.go
    PrepareForMerge/Merge, and the workflow-pr callers).

The workflow-pr callers now clone the base repo by head.Sha and, where a
named head branch is expected downstream, check it out explicitly
(checkout -f -B <prjGitPRbranch> <head.Sha>). Push behaviour is
unchanged: the base repo for bot-created PRs, or the fork URL (guarded
by AllowMaintainerEdit) for PRs opened from a fork. No read access to
the source fork remains anywhere.

The unit tests are updated to expect the now-visible GitClone and
checkout calls. This does not add any git/Gitea operations: the previous
FetchPRHeadFromTarget helper already performed the same clone and
checkout internally, so folding it into GitClone only makes those
existing calls explicit at the call sites (and thus in the mocks).

Assisted-by: Anthropic:claude-opus-4.8
This commit is contained in:
2026-07-20 14:49:37 +02:00
parent c07541fd2a
commit 09ba73faf4
8 changed files with 93 additions and 201 deletions
+12 -46
View File
@@ -35,8 +35,6 @@ import (
"time"
"golang.org/x/sync/errgroup"
"src.opensuse.org/autogits/common/gitea-generated/models"
)
//go:generate mockgen -source=git_utils.go -destination=mock/git_utils.go -typed
@@ -75,9 +73,6 @@ type Git interface {
// error if git, but wrong remote
GitClone(repo, branch, remoteUrl string) (string, error) // clone, or check if path is already checked out remote and force pulls, error otherwise. Return remotename, errror
GitCloneLocal(repoPath, otherRepoPath string) (string, error)
// FetchPRHeadFromTarget materializes a PR head from its base repo via refs/pull/<index>/head
// (no access to the source fork needed). Returns the base remote name.
FetchPRHeadFromTarget(repo, localBranch string, pr *models.PullRequest) (string, error)
GitParseCommits(cwd string, commitIDs []string) (parsedCommits []GitCommit, err error)
GitCatFile(cwd, commitId, filename string) (data []byte, err error)
@@ -382,55 +377,26 @@ func (e *GitHandlerImpl) GitClone(repo, branch, remoteUrl string) (string, error
e.GitExecOrPanic(repo, args...)
localBranch := sanitizeRef(branch)
if strings.HasPrefix(branch, "refs/") {
if strings.HasPrefix(branch, "refs/") || isGitObjectID(branch) {
return remoteName, e.GitExec(repo, "checkout", "-f", "-B", localBranch, "FETCH_HEAD")
}
return remoteName, e.GitExec(repo, "checkout", "-f", "--track", "-B", localBranch, remoteRef)
}
// PullRequestHeadRef returns the git reference under which a forge (Gitea/GitHub)
// exposes the head commit of pull request `index` inside its *base* repository,
// e.g. "refs/pull/42/head". This commit is reachable from the base repository
// without any access to the (possibly private) source fork.
func PullRequestHeadRef(index int64) string {
return fmt.Sprintf("refs/pull/%d/head", index)
}
// FetchPRHeadFromTarget materializes the head commit of a pull request into `repo`
// (checked out as local branch `localBranch`) by fetching it from the pull
// request's *base/target* repository via refs/pull/<index>/head, instead of
// cloning the source fork (which may be private and unreadable by the bot).
//
// It returns the remote name of the base repository (as produced by GitClone),
// so callers can reuse it (e.g. for further fetches of the base branch). The
// fetched FETCH_HEAD equals pr.Head.Sha.
func (e *GitHandlerImpl) FetchPRHeadFromTarget(repo, localBranch string, pr *models.PullRequest) (remoteName string, err error) {
if pr == nil || pr.Base == nil || pr.Base.Repo == nil {
return "", fmt.Errorf("FetchPRHeadFromTarget: incomplete PR data (nil base repo)")
// isGitObjectID reports whether ref is a full git object id (sha1 or sha256),
// i.e. a fetched commit that must be checked out from FETCH_HEAD rather than a
// remote-tracking branch.
func isGitObjectID(ref string) bool {
if len(ref) != 40 && len(ref) != 64 {
return false
}
baseUrl := pr.Base.Repo.SSHURL
if len(baseUrl) == 0 {
return "", fmt.Errorf("FetchPRHeadFromTarget: base repo has no SSHURL")
}
headRef := PullRequestHeadRef(pr.Index)
// GitClone with a "refs/..." branch clones/updates the base repo and checks
// out FETCH_HEAD into a local branch named after the sanitized ref.
remoteName, err = e.GitClone(repo, headRef, baseUrl)
if err != nil {
return remoteName, err
}
// Re-point the checkout to the caller-requested local branch name. GitClone
// leaves us on sanitizeRef(headRef) (e.g. "pull/<idx>/head"); callers expect
// the PR's head branch name.
if len(localBranch) > 0 && localBranch != sanitizeRef(headRef) {
if err := e.GitExec(repo, "checkout", "-f", "-B", localBranch, "FETCH_HEAD"); err != nil {
return remoteName, err
for _, c := range ref {
isHex := c >= '0' && c <= '9' || c >= 'a' && c <= 'f'
if !isHex {
return false
}
}
return remoteName, nil
return true
}
func (e *GitHandlerImpl) GitCloneLocal(gitDir, repoPath string) (remoteName string, err error) {
+9 -58
View File
@@ -31,8 +31,6 @@ import (
"sync"
"testing"
"time"
"src.opensuse.org/autogits/common/gitea-generated/models"
)
func TestGitClone(t *testing.T) {
@@ -1584,28 +1582,10 @@ func TestSanitizeRef(t *testing.T) {
}
}
// TestPullRequestHeadRef verifies the refs/pull/<index>/head reference builder.
func TestPullRequestHeadRef(t *testing.T) {
tests := []struct {
index int64
expected string
}{
{1, "refs/pull/1/head"},
{42, "refs/pull/42/head"},
{12345, "refs/pull/12345/head"},
}
for _, test := range tests {
if got := PullRequestHeadRef(test.index); got != test.expected {
t.Errorf("PullRequestHeadRef(%d) = %q; want %q", test.index, got, test.expected)
}
}
}
// TestFetchPRHeadFromTarget verifies that FetchPRHeadFromTarget materializes a PR head
// from the *base* repository via refs/pull/<index>/head, without ever touching the source
// fork. It creates a real base repo, publishes a commit under refs/pull/7/head (as Gitea does
// when a PR is opened), and asserts the fetched content.
func TestFetchPRHeadFromTarget(t *testing.T) {
// TestGitCloneBySha verifies that GitClone can materialize a commit given by its
// object id (SHA), checking it out from FETCH_HEAD. This is how a PR head is read
// from the base repository without touching the source fork.
func TestGitCloneBySha(t *testing.T) {
d := t.TempDir()
runGit := func(dir string, args ...string) string {
t.Helper()
@@ -1619,7 +1599,6 @@ func TestFetchPRHeadFromTarget(t *testing.T) {
return strings.TrimSpace(string(out))
}
// Create the base/target repository with a default branch.
baseDir := filepath.Join(d, "base")
os.MkdirAll(baseDir, 0755)
runGit(baseDir, "init", "-b", "main")
@@ -1628,18 +1607,10 @@ func TestFetchPRHeadFromTarget(t *testing.T) {
os.WriteFile(filepath.Join(baseDir, "base.txt"), []byte("base content"), 0644)
runGit(baseDir, "add", "base.txt")
runGit(baseDir, "commit", "-m", "base init")
// Simulate the PR head commit that Gitea publishes into the base repo under
// refs/pull/7/head. We make it on a detached branch so it is only reachable via that ref.
runGit(baseDir, "checkout", "-b", "pr-head")
os.WriteFile(filepath.Join(baseDir, "pr.txt"), []byte("pr content"), 0644)
runGit(baseDir, "add", "pr.txt")
runGit(baseDir, "commit", "-m", "pr head commit")
headSha := runGit(baseDir, "rev-parse", "HEAD")
runGit(baseDir, "update-ref", "refs/pull/7/head", headSha)
// Move back and delete the helper branch so the commit is ONLY reachable via refs/pull/7/head.
runGit(baseDir, "checkout", "main")
runGit(baseDir, "branch", "-D", "pr-head")
gh, err := AllocateGitWorkTree(d, "Test", "test@example.com")
if err != nil {
@@ -1652,41 +1623,21 @@ func TestFetchPRHeadFromTarget(t *testing.T) {
}
defer g.Close()
// The PR points its base repo at the base dir. Head.Repo is intentionally left nil to
// prove that the source fork is never accessed.
pr := &models.PullRequest{
Index: 7,
Base: &models.PRBranchInfo{
Name: "main",
Repo: &models.Repository{SSHURL: "file://" + baseDir},
},
Head: &models.PRBranchInfo{
Sha: headSha,
},
}
repo := path.Join(config.GetPrjGitLocalDir(), "checkout")
remote, err := g.FetchPRHeadFromTarget(repo, "pr-branch", pr)
remote, err := g.GitClone(repo, headSha, "file://"+baseDir)
if err != nil {
t.Fatalf("FetchPRHeadFromTarget failed: %v", err)
t.Fatalf("GitClone by sha failed: %v", err)
}
if len(remote) == 0 {
t.Fatal("expected a non-empty base remote name")
t.Fatal("expected a non-empty remote name")
}
// The local branch must point at the PR head SHA.
gotSha, err := g.GitBranchHead(repo, "pr-branch")
gotSha, err := g.GitBranchHead(repo, headSha)
if err != nil {
t.Fatalf("GitBranchHead failed: %v", err)
}
if gotSha != headSha {
t.Errorf("local branch head = %q; want PR head %q", gotSha, headSha)
}
// The PR-only content must be present in the checkout.
fullRepo := filepath.Join(g.GetPath(), repo)
if _, err := os.Stat(filepath.Join(fullRepo, "pr.txt")); err != nil {
t.Errorf("expected pr.txt materialized from refs/pull/7/head: %v", err)
t.Errorf("checked out head = %q; want %q", gotSha, headSha)
}
}
-40
View File
@@ -15,7 +15,6 @@ import (
gomock "go.uber.org/mock/gomock"
common "src.opensuse.org/autogits/common"
models "src.opensuse.org/autogits/common/gitea-generated/models"
)
// MockGitSubmoduleLister is a mock of GitSubmoduleLister interface.
@@ -548,45 +547,6 @@ func (c *MockGitContextCall) DoAndReturn(f func() context.Context) *MockGitConte
return c
}
// FetchPRHeadFromTarget mocks base method.
func (m *MockGit) FetchPRHeadFromTarget(repo, localBranch string, pr *models.PullRequest) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchPRHeadFromTarget", repo, localBranch, pr)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FetchPRHeadFromTarget indicates an expected call of FetchPRHeadFromTarget.
func (mr *MockGitMockRecorder) FetchPRHeadFromTarget(repo, localBranch, pr any) *MockGitFetchPRHeadFromTargetCall {
mr.mock.ctrl.T.Helper()
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchPRHeadFromTarget", reflect.TypeOf((*MockGit)(nil).FetchPRHeadFromTarget), repo, localBranch, pr)
return &MockGitFetchPRHeadFromTargetCall{Call: call}
}
// MockGitFetchPRHeadFromTargetCall wrap *gomock.Call
type MockGitFetchPRHeadFromTargetCall struct {
*gomock.Call
}
// Return rewrite *gomock.Call.Return
func (c *MockGitFetchPRHeadFromTargetCall) Return(arg0 string, arg1 error) *MockGitFetchPRHeadFromTargetCall {
c.Call = c.Call.Return(arg0, arg1)
return c
}
// Do rewrite *gomock.Call.Do
func (c *MockGitFetchPRHeadFromTargetCall) Do(f func(string, string, *models.PullRequest) (string, error)) *MockGitFetchPRHeadFromTargetCall {
c.Call = c.Call.Do(f)
return c
}
// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockGitFetchPRHeadFromTargetCall) DoAndReturn(f func(string, string, *models.PullRequest) (string, error)) *MockGitFetchPRHeadFromTargetCall {
c.Call = c.Call.DoAndReturn(f)
return c
}
// GetPath mocks base method.
func (m *MockGit) GetPath() string {
m.ctrl.T.Helper()
+4 -4
View File
@@ -753,7 +753,7 @@ func (rs *PRSet) PrepareForMerge(git Git) error {
}
continue
}
git.GitExecOrPanic(repoPath, "fetch", remote, PullRequestHeadRef(prinfo.PR.Index))
git.GitExecOrPanic(repoPath, "fetch", remote, head.Sha)
prMergeable := true
switch rs.Config.MergeMode {
case MergeModeFF:
@@ -813,7 +813,7 @@ func (rs *PRSet) PrepareForMerge(git Git) error {
}
testBranch := fmt.Sprintf("PR_%d_mergetest", prjgit.Index)
git.GitExecOrPanic(prjPath, "fetch", remote, PullRequestHeadRef(prjgit.Index))
git.GitExecOrPanic(prjPath, "fetch", remote, prjgit.Head.Sha)
if err := git.GitExec(prjPath, "checkout", "-B", testBranch, prjgit.Base.Sha); err != nil {
return err
@@ -899,7 +899,7 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequesterAndMerger, git Git) error {
remote, err := git.GitClone(prjPath, prjgitBranch, prjgit.Base.Repo.SSHURL)
PanicOnError(err)
git.GitExecOrPanic(prjPath, "fetch", remote, PullRequestHeadRef(prjgit.Index))
git.GitExecOrPanic(prjPath, "fetch", remote, prjgit.Head.Sha)
msg := fmt.Sprintf("M: %s\n\nPR: %s",
strings.TrimPrefix(prjgit.Title, "Forwarded PRs: "),
@@ -979,7 +979,7 @@ func (rs *PRSet) Merge(gitea GiteaReviewUnrequesterAndMerger, git Git) error {
}
prinfo.RemoteName, err = git.GitClone(repoPath, br, repo.SSHURL)
PanicOnError(err)
git.GitExecOrPanic(repoPath, "fetch", prinfo.RemoteName, PullRequestHeadRef(prinfo.PR.Index))
git.GitExecOrPanic(repoPath, "fetch", prinfo.RemoteName, prinfo.PR.Head.Sha)
LogDebugCtx(rs.Context(), "MergeMode is", rs.Config.MergeMode)
if rs.Config.MergeMode == MergeModeDevel || isNewRepo {
git.GitExecOrPanic(repoPath, "checkout", "-B", br, head.Sha)
+2 -2
View File
@@ -66,7 +66,7 @@ func TestPRSet_Merge_Special(t *testing.T) {
// Mock expectations for Merge
// Clone and fetch for PrjGit
mockGit.EXPECT().GitClone(prjPath, "main", "prj-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(prjPath, "fetch", "origin", common.PullRequestHeadRef(prjGitPR.Index))
mockGit.EXPECT().GitExecOrPanic(prjPath, "fetch", "origin", prjGitPR.Head.Sha)
// mockGit.EXPECT().GitExecWithOutputOrPanic(prjPath, "merge-base", "HEAD", "base-sha", "prj-head-sha").Return("base-sha")
mockGit.EXPECT().GitExec(prjPath, "merge", "--no-ff", "-m", gomock.Any(), "prj-head-sha").Return(nil)
@@ -76,7 +76,7 @@ func TestPRSet_Merge_Special(t *testing.T) {
// Clone and fetch for new-pkg
mockGit.EXPECT().GitClone("new-pkg", "main", "pkg-ssh-url").Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic("new-pkg", "fetch", "origin", common.PullRequestHeadRef(newPkgPR.Index))
mockGit.EXPECT().GitExecOrPanic("new-pkg", "fetch", "origin", newPkgPR.Head.Sha)
mockGit.EXPECT().GitExecOrPanic("new-pkg", "checkout", "-B", "main", "pkg-head-sha")
// Pushing changes
+27 -27
View File
@@ -1848,7 +1848,7 @@ func TestPRPrepareForMerge(t *testing.T) {
},
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "checkout", "-B", "PR_1_mergetest", prjPR.Base.Sha).Return(nil)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "--no-commit", prjPR.Head.Sha).Return(nil)
},
@@ -1863,11 +1863,11 @@ func TestPRPrepareForMerge(t *testing.T) {
},
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(nil)
m.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "checkout", "-B", "PR_1_mergetest", prjPR.Base.Sha).Return(nil)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "--no-commit", prjPR.Head.Sha).Return(nil)
},
@@ -1883,11 +1883,11 @@ func TestPRPrepareForMerge(t *testing.T) {
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
pkgPR.Labels = append(pkgPR.Labels, &models.Label{Name: common.Label_NewRepository})
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(nil)
m.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "checkout", "-B", "PR_1_mergetest", prjPR.Base.Sha).Return(nil)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "--no-commit", prjPR.Head.Sha).Return(nil)
},
@@ -1902,7 +1902,7 @@ func TestPRPrepareForMerge(t *testing.T) {
},
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
// merge-base fails initially
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(fmt.Errorf("not ancestor"))
// HasMerge returns true
@@ -1910,7 +1910,7 @@ func TestPRPrepareForMerge(t *testing.T) {
m.EXPECT().GitExecWithOutput("pkg", "rev-parse", "HEAD").Return("target_head", nil)
m.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "checkout", "-B", "PR_1_mergetest", prjPR.Base.Sha).Return(nil)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "--no-commit", prjPR.Head.Sha).Return(nil)
},
@@ -1925,11 +1925,11 @@ func TestPRPrepareForMerge(t *testing.T) {
},
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(nil)
m.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "checkout", "-B", "PR_1_mergetest", prjPR.Base.Sha).Return(nil)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "--no-commit", prjPR.Head.Sha).Return(fmt.Errorf("conflict"))
m.EXPECT().GitResolveConflicts("_ObsPrj/🚀org🈴_ObsPrj🈴master", prjPR.MergeBase, prjPR.Base.Sha, prjPR.Head.Sha).Return(fmt.Errorf("conflict"))
@@ -1946,7 +1946,7 @@ func TestPRPrepareForMerge(t *testing.T) {
},
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(fmt.Errorf("not ancestor"))
},
expected: false,
@@ -1961,7 +1961,7 @@ func TestPRPrepareForMerge(t *testing.T) {
editable: true,
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
// First merge-base fails
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(fmt.Errorf("not ancestor"))
// HasMerge returns false
@@ -1978,7 +1978,7 @@ func TestPRPrepareForMerge(t *testing.T) {
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", "new_pkg_head_sha").Return(nil)
m.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "checkout", "-B", "PR_1_mergetest", prjPR.Base.Sha).Return(nil)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "--no-commit", prjPR.Head.Sha).Return(nil)
},
@@ -1994,7 +1994,7 @@ func TestPRPrepareForMerge(t *testing.T) {
editable: true,
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
// First merge-base fails
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(fmt.Errorf("not ancestor"))
// HasMerge returns false
@@ -2017,7 +2017,7 @@ func TestPRPrepareForMerge(t *testing.T) {
editable: false,
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
// First merge-base fails
m.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(fmt.Errorf("not ancestor"))
// HasMerge returns false
@@ -2110,11 +2110,11 @@ func TestPRMergeMock(t *testing.T) {
},
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "-m", gomock.Any(), prjPR.Head.Sha).Return(nil)
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin_pkg", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin_pkg", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin_pkg", pkgPR.Head.Sha)
m.EXPECT().GitExecOrPanic("pkg", "merge", "--ff-only", pkgPR.Head.Sha)
m.EXPECT().GitExecOrPanic("pkg", "push", "origin_pkg")
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "push", "origin")
@@ -2130,11 +2130,11 @@ func TestPRMergeMock(t *testing.T) {
},
setup: func(m *mock_common.MockGit, prjPR, pkgPR *models.PullRequest) {
m.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
m.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "-m", gomock.Any(), prjPR.Head.Sha).Return(nil)
m.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin_pkg", nil)
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin_pkg", common.PullRequestHeadRef(pkgPR.Index))
m.EXPECT().GitExecOrPanic("pkg", "fetch", "origin_pkg", pkgPR.Head.Sha)
m.EXPECT().GitExecOrPanic("pkg", "checkout", "-B", "master", pkgPR.Head.Sha)
m.EXPECT().GitExecOrPanic("pkg", "push", "-f", "origin_pkg")
m.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "push", "origin")
@@ -2250,11 +2250,11 @@ func TestPRMerge_NewRepositoryReferencedIssueRepositoryNil(t *testing.T) {
// Set expectations on git and verify expectedPrjPath and expectedPkgPath are used
git.EXPECT().GitClone(expectedPrjPath, "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
git.EXPECT().GitExecOrPanic(expectedPrjPath, "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
git.EXPECT().GitExecOrPanic(expectedPrjPath, "fetch", "origin", prjPR.Head.Sha)
git.EXPECT().GitExec(expectedPrjPath, "merge", "--no-ff", "-m", gomock.Any(), prjPR.Head.Sha).Return(nil)
git.EXPECT().GitClone(expectedPkgPath, "master", pkgPR.Base.Repo.SSHURL).Return("origin_pkg", nil)
git.EXPECT().GitExecOrPanic(expectedPkgPath, "fetch", "origin_pkg", common.PullRequestHeadRef(pkgPR.Index))
git.EXPECT().GitExecOrPanic(expectedPkgPath, "fetch", "origin_pkg", pkgPR.Head.Sha)
git.EXPECT().GitExecOrPanic(expectedPkgPath, "checkout", "-B", "master", pkgPR.Head.Sha)
git.EXPECT().GitExecOrPanic(expectedPkgPath, "push", "-f", "origin_pkg", "pkg_head_sha:master")
@@ -2340,11 +2340,11 @@ func TestPRMerge_GetPullRequestFetchError(t *testing.T) {
// Set expectations on git
git.EXPECT().GitClone(expectedPrjPath, "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
git.EXPECT().GitExecOrPanic(expectedPrjPath, "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
git.EXPECT().GitExecOrPanic(expectedPrjPath, "fetch", "origin", prjPR.Head.Sha)
git.EXPECT().GitExec(expectedPrjPath, "merge", "--no-ff", "-m", gomock.Any(), prjPR.Head.Sha).Return(nil)
git.EXPECT().GitClone(expectedPkgPath, "master", pkgPR.Base.Repo.SSHURL).Return("origin_pkg", nil)
git.EXPECT().GitExecOrPanic(expectedPkgPath, "fetch", "origin_pkg", common.PullRequestHeadRef(pkgPR.Index))
git.EXPECT().GitExecOrPanic(expectedPkgPath, "fetch", "origin_pkg", pkgPR.Head.Sha)
git.EXPECT().GitExecOrPanic(expectedPkgPath, "checkout", "-B", "master", pkgPR.Head.Sha)
git.EXPECT().GitExecOrPanic(expectedPkgPath, "push", "-f", "origin_pkg", "pkg_head_sha:master")
@@ -2688,12 +2688,12 @@ func TestPRFieldsApprovedAndMergeable(t *testing.T) {
// Expectations for pkgPR (index 2)
gitMock.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
gitMock.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
gitMock.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
gitMock.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(nil)
// Expectations for prjPR (index 1)
gitMock.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
gitMock.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
gitMock.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
gitMock.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "checkout", "-B", "PR_1_mergetest", prjPR.Base.Sha).Return(nil)
gitMock.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "merge", "--no-ff", "--no-commit", prjPR.Head.Sha).Return(nil)
@@ -2736,7 +2736,7 @@ func TestPRFieldsApprovedAndMergeable(t *testing.T) {
// Package merge check fails
gitMock.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
gitMock.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
gitMock.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
gitMock.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(fmt.Errorf("conflict"))
prjInfo.Mergeable = true
@@ -2776,12 +2776,12 @@ func TestPRFieldsApprovedAndMergeable(t *testing.T) {
// Expectations for pkgPR (succeeds)
gitMock.EXPECT().GitClone("pkg", "master", pkgPR.Base.Repo.SSHURL).Return("origin", nil)
gitMock.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", common.PullRequestHeadRef(pkgPR.Index))
gitMock.EXPECT().GitExecOrPanic("pkg", "fetch", "origin", pkgPR.Head.Sha)
gitMock.EXPECT().GitExec("pkg", "merge-base", "--is-ancestor", "HEAD", pkgPR.Head.Sha).Return(nil)
// Expectations for prjPR (fails to merge)
gitMock.EXPECT().GitClone("_ObsPrj/🚀org🈴_ObsPrj🈴master", "master", prjPR.Base.Repo.SSHURL).Return("origin", nil)
gitMock.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", common.PullRequestHeadRef(prjPR.Index))
gitMock.EXPECT().GitExecOrPanic("_ObsPrj/🚀org🈴_ObsPrj🈴master", "fetch", "origin", prjPR.Head.Sha)
gitMock.EXPECT().GitExec("_ObsPrj/🚀org🈴_ObsPrj🈴master", "checkout", "-B", "PR_1_mergetest", prjPR.Base.Sha).Return(nil)
// git merge returns error (conflict)
+5 -3
View File
@@ -741,7 +741,7 @@ func (prp *PRProcessor) UpdatePrjGitPR(prset *common.PRSet, rebase bool) error {
git := prp.git
prjPath := prp.getPrjPath()
if len(prset.PRs) == 1 {
PrjGitPR.RemoteName, err = git.FetchPRHeadFromTarget(prjPath, "", PrjGitPR.PR)
PrjGitPR.RemoteName, err = git.GitClone(prjPath, PrjGitPR.PR.Head.Sha, PrjGitPR.PR.Base.Repo.SSHURL)
common.PanicOnError(err)
common.LogDebugCtx(prp.ctx, "Only project git in PR. Nothing to update.")
return nil
@@ -750,8 +750,9 @@ func (prp *PRProcessor) UpdatePrjGitPR(prset *common.PRSet, rebase bool) error {
PrjGit := PrjGitPR.PR.Base.Repo
prjGitPRbranch := PrjGitPR.PR.Head.Ref
PrjGitPR.RemoteName, err = git.FetchPRHeadFromTarget(prjPath, prjGitPRbranch, PrjGitPR.PR)
PrjGitPR.RemoteName, err = git.GitClone(prjPath, PrjGitPR.PR.Head.Sha, PrjGitPR.PR.Base.Repo.SSHURL)
common.PanicOnError(err)
git.GitExecOrPanic(prjPath, "checkout", "-f", "-B", prjGitPRbranch, PrjGitPR.PR.Head.Sha)
git.GitExecOrPanic(prjPath, "fetch", PrjGitPR.RemoteName, PrjGitBranch)
headCommit, err := git.GitBranchHead(prjPath, prjGitPRbranch)
@@ -914,8 +915,9 @@ func (prp *PRProcessor) Process(ctx context.Context, req *models.PullRequest) (e
if prjGitPR.PR.State != common.GiteaPRState_Open {
if prjGitPR.PR.HasMerged {
prjPath := prp.getPrjPath()
prjGitPR.RemoteName, err = git.FetchPRHeadFromTarget(prjPath, prjGitPRbranch, prjGitPR.PR)
prjGitPR.RemoteName, err = git.GitClone(prjPath, prjGitPR.PR.Head.Sha, prjGitPR.PR.Base.Repo.SSHURL)
common.PanicOnError(err)
git.GitExecOrPanic(prjPath, "checkout", "-f", "-B", prjGitPRbranch, prjGitPR.PR.Head.Sha)
old_pkgs, err := git.GitSubmoduleList(prjPath, prjGitPR.PR.MergeBase)
common.PanicOnError(err)
+34 -21
View File
@@ -153,7 +153,7 @@ func TestAllocatePRProcessor(t *testing.T) {
mockGitGen.EXPECT().ReadExistingPathWithPrjCtx(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGit, nil).AnyTimes()
mockGit.EXPECT().GetPath().Return("/tmp/test").AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitCloneLocal(gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().Close().Return(nil).AnyTimes()
@@ -445,7 +445,7 @@ func TestUpdatePrjGitPR(t *testing.T) {
},
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(prjPath, "", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(prjPath, gomock.Any(), gomock.Any()).Return("origin", nil)
err := processor.UpdatePrjGitPR(prset, false)
if err != nil {
t.Errorf("Unexpected error: %v", err)
@@ -475,7 +475,7 @@ func TestUpdatePrjGitPR(t *testing.T) {
},
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(prjPath, "", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(prjPath, gomock.Any(), gomock.Any()).Return("origin", nil)
err := processor.UpdatePrjGitPR(prset, false)
if err != nil {
t.Errorf("Unexpected error: %v", err)
@@ -528,7 +528,8 @@ func TestUpdatePrjGitPR(t *testing.T) {
},
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(prjPath, gomock.Any(), gomock.Any()).Return("remote2", nil)
mockGit.EXPECT().GitClone(prjPath, gomock.Any(), gomock.Any()).Return("remote2", nil)
mockGit.EXPECT().GitExecOrPanic(prjPath, "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(prjPath, "fetch", "remote2", "main")
mockGit.EXPECT().GitBranchHead(prjPath, gomock.Any()).Return("sha1", nil).Times(2)
mockGit.EXPECT().GitSubmoduleList(prjPath, "HEAD").Return(map[string]string{"other-pkg": "other-sha"}, nil)
@@ -585,7 +586,8 @@ func TestUpdatePrjGitPR(t *testing.T) {
},
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "feature", gomock.Any()).Return("remote2", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("remote2", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "remote2", "main")
mockGit.EXPECT().GitBranchHead(gomock.Any(), "feature").Return("sha1", nil).Times(1)
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"other-pkg": "sha1"}, nil)
@@ -643,7 +645,8 @@ func TestUpdatePrjGitPR(t *testing.T) {
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", gomock.Any(), gomock.Any())
// Rebase expectations
mockGit.EXPECT().GitExec(gomock.Any(), "rebase", gomock.Any()).Return(nil)
@@ -718,7 +721,8 @@ func TestUpdatePrjGitPR(t *testing.T) {
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
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)
@@ -782,7 +786,7 @@ func TestCreatePRjGitPR_Integration(t *testing.T) {
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().FetchPRHeadFromTarget(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitStatus(gomock.Any()).Return(nil, nil).AnyTimes()
mockGit.EXPECT().GetPath().Return("/tmp").AnyTimes()
@@ -936,8 +940,9 @@ func TestPRProcessor_Process_EdgeCases(t *testing.T) {
// 1. Clone PrjGit using Base URL (called by PrepareForMerge)
mockGit.EXPECT().GitClone(gomock.Any(), "main", "base-url").Return("origin", nil)
// 2. Materialize PrjGit PR head from the target repo via refs/pull (merged path in Process)
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "PR_branch", gomock.Any()).Return("origin", nil)
// 2. Materialize PrjGit PR head from the target repo via head.Sha (merged path in Process)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any()).AnyTimes()
// 3. List submodules for merge base and head branch
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "merge-base-sha").Return(map[string]string{"pkg-a": "old-sha"}, nil)
@@ -949,6 +954,7 @@ func TestPRProcessor_Process_EdgeCases(t *testing.T) {
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
mockGit.EXPECT().GitExec(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
// Downstream update expectations
@@ -994,7 +1000,7 @@ func TestPRProcessor_Process_EdgeCases(t *testing.T) {
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().FetchPRHeadFromTarget(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
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()
@@ -1066,7 +1072,7 @@ func TestPRProcessor_Process_GitmodulesHandling(t *testing.T) {
// Standard update calls within Process
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
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()
@@ -1154,7 +1160,7 @@ func TestPRProcessor_Process_GitmodulesHandling(t *testing.T) {
// Standard update calls within Process
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any()).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{"pkg1": "pkg-sha"}, nil).AnyTimes()
@@ -1356,7 +1362,7 @@ func TestProcessFunc(t *testing.T) {
Gitea = gitea
mockGit := mock_common.NewMockGitCtx(ctl)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitCloneLocal(gomock.Any(), gomock.Any()).Return("origin", nil).AnyTimes()
mockGit.EXPECT().GitExecWithOutputOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("sha1\nsha2").AnyTimes()
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
@@ -1528,7 +1534,8 @@ func TestUpdatePrjGitPR_MismatchBeforeSubmoduleUpdate(t *testing.T) {
common.IsDryRun = true
defer func() { common.IsDryRun = false }()
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "PR_branch", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "origin", "main")
// GitBranchHead returns something different from "old-head"
@@ -1610,7 +1617,8 @@ func TestUpdatePrjGitPR_SuccessfulSubmoduleUpdateAfterCheck(t *testing.T) {
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "PR_branch", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "origin", "main")
// GitBranchHead returns EXACTLY "matching-head"
@@ -1701,7 +1709,8 @@ func TestUpdatePrjGitPR_ValidRebaseNowPasses(t *testing.T) {
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "PR_branch", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "origin", "main")
// GitBranchHead is called BEFORE rebase and returns original-sha (matches PR)
@@ -1788,7 +1797,8 @@ func TestUpdatePrjGitPR_MismatchCaughtBeforeRebase(t *testing.T) {
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "PR_branch", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "origin", "main")
// GitBranchHead returns mismatching-sha BEFORE rebase
@@ -1869,7 +1879,8 @@ func TestUpdatePrjGitPR_RebaseOnlyChangeIsPushed(t *testing.T) {
},
}
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "PR_branch", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "origin", "main")
// 1. Fetch headCommit BEFORE rebase
@@ -1965,7 +1976,8 @@ func TestUpdatePrjGitPR_ForkPushGoesToFork(t *testing.T) {
}
// Head is read from the TARGET (base repo) via refs/pull, returning the base remote name.
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "PR_branch", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "origin", "main")
// headCommit before rebase
mockGit.EXPECT().GitBranchHead(gomock.Any(), "PR_branch").Return("original-sha", nil).Times(1)
@@ -2198,7 +2210,8 @@ func TestUpdatePrjGitPR_ForkPermissionError(t *testing.T) {
}
prset.PRs[0].RemoteName = "origin"
mockGit.EXPECT().FetchPRHeadFromTarget(gomock.Any(), "fork-ref", gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitClone(gomock.Any(), gomock.Any(), gomock.Any()).Return("origin", nil)
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "checkout", "-f", "-B", gomock.Any(), gomock.Any())
mockGit.EXPECT().GitExecOrPanic(gomock.Any(), "fetch", "origin", "main")
mockGit.EXPECT().GitBranchHead(gomock.Any(), gomock.Any()).Return("old-sha", nil).Times(1)
mockGit.EXPECT().GitSubmoduleList(gomock.Any(), "HEAD").Return(map[string]string{"pkg-a": "old-sha"}, nil)