3 Commits

Author SHA256 Message Date
a8337693ce pr: use relative paths in new submodules
All checks were successful
go-generate-check / go-generate-check (pull_request) Successful in 23s
Integration tests / t (pull_request) Successful in 7m54s
2026-03-04 08:45:24 +01:00
45275cb004 direct: use relative paths when adding repositories and repocheck 2026-03-04 08:23:17 +01:00
d023627f42 common: Add relative path repository resolution helper 2026-03-04 08:23:09 +01:00
5 changed files with 179 additions and 15 deletions

View File

@@ -44,6 +44,42 @@ type NewRepos struct {
const maintainership_line = "MAINTAINER"
var true_lines []string = []string{"1", "TRUE", "YES", "OK", "T"}
var InvalidUrlError error = errors.New("PrjGit or PackageGit URLs cannot be empty.")
var AbsoluteUrlError error = errors.New("PrjGit or PackageGit URLs cannot be relative.")
var HostsNotEqualError error = errors.New("PrjGit or PackageGit are not the same hosts.")
var AbsoluteUrlWithQuery error = errors.New("PrjGit or PackageGit with query parameter. Unsupported.")
var InvalidPath error = errors.New("PrjGit or PackageGit path has unsupported format.")
func RelativeRepositoryPath(prjgit_org, packagegit string) (string, error) {
if len(packagegit) == 0 {
return "", InvalidUrlError
}
pkggiturl, err := url.Parse(packagegit)
if err != nil {
return "", err
}
if !pkggiturl.IsAbs() {
return "", AbsoluteUrlError
}
if len(pkggiturl.RawQuery) != 0 {
return "", AbsoluteUrlWithQuery
}
pkggitpath := SplitStringNoEmpty(pkggiturl.Path, "/")
if len(pkggitpath) != 2 {
return "", InvalidPath
}
pkggitpath[1] = strings.TrimSuffix(pkggitpath[1], ".git")
if prjgit_org == pkggitpath[0] {
return "../" + pkggitpath[1], nil
}
return "../../" + pkggitpath[0] + "/" + pkggitpath[1], nil
}
func HasSpace(s string) bool {
return strings.IndexFunc(s, unicode.IsSpace) >= 0

View File

@@ -8,6 +8,82 @@ import (
"src.opensuse.org/autogits/common"
)
func TestRelativeRepositoryPath(t *testing.T) {
tests := []struct {
name string
prjorg, repo string
hasError bool
relative string
}{
{
name: "Empty packagegit",
prjorg: "org1",
repo: "",
hasError: true,
},
{
name: "Invalid URL",
prjorg: "org1",
repo: ":",
hasError: true,
},
{
name: "Relative packagegit",
prjorg: "org1",
repo: "/path/to/repo",
hasError: true,
},
{
name: "Packagegit with query",
prjorg: "org1",
repo: "https://host/org1/repo?query=1",
hasError: true,
},
{
name: "Invalid path (too short)",
prjorg: "org1",
repo: "https://host/repo",
hasError: true,
},
{
name: "Invalid path (too long)",
prjorg: "org1",
repo: "https://host/org/repo/extra",
hasError: true,
},
{
name: "Same org",
prjorg: "org1",
repo: "https://host/org1/repo.git",
relative: "../repo",
},
{
name: "Different org",
prjorg: "org1",
repo: "https://host/org2/repo.git",
relative: "../../org2/repo",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
r, err := common.RelativeRepositoryPath(test.prjorg, test.repo)
if err != nil && !test.hasError {
t.Error("Expected no error but have one", err)
}
if err == nil && test.hasError {
t.Error("Expected an error but had none. Returned:", r)
}
if err == nil && test.relative != r {
t.Error("Expected", test.relative, "but have", r)
}
})
}
}
func TestGitUrlParse(t *testing.T) {
tests := []struct {
name string

View File

@@ -19,6 +19,7 @@ package main
*/
import (
"bytes"
"flag"
"fmt"
"io/fs"
@@ -123,7 +124,9 @@ func processConfiguredRepositoryAction(action *common.RepositoryWebhookEvent, co
common.LogError(" - ", action.Repository.Name, "repo is not sha256. Ignoring.")
return
}
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name))
relpath, err := common.RelativeRepositoryPath(gitOrg, action.Repository.Clone_Url)
common.PanicOnError(err)
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", relpath, action.Repository.Name))
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
branch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, action.Repository.Name), "branch", "--show-current"))
@@ -268,6 +271,12 @@ func verifyProjectState(git common.Git, org string, config *common.AutogitConfig
sub, err := git.GitSubmoduleList(gitPrj, "HEAD")
common.PanicOnError(err)
submodulesData, err := git.GitCatFile(gitPrj, "HEAD", ".gitmodules")
var submoduleEntries []common.Submodule
if err == nil {
submoduleEntries, _ = common.ParseSubmodulesFile(bytes.NewReader(submodulesData))
}
common.LogDebug(" * Getting package links")
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 {
@@ -395,8 +404,27 @@ next_repo:
}
// }
relpath, err := common.RelativeRepositoryPath(gitOrg, r.CloneURL)
common.PanicOnError(err)
for repo := range sub {
if repo == r.Name {
// verify we are using relative repository paths, and if not, adjust them
sidx := slices.IndexFunc(submoduleEntries, func(s common.Submodule) bool {
if path.Base(s.Path) == r.Name {
return true
}
return false
})
if sidx >= 0 && submoduleEntries[sidx].Url != relpath {
submoduleEntries[sidx].Url = relpath
f, err := os.OpenFile(path.Join(git.GetPath(), ".gitmodules"), os.O_CREATE|os.O_TRUNC, 0o6400)
common.PanicOnError(err)
defer f.Close()
common.PanicOnError(common.WriteSubmodules(submoduleEntries, f))
isGitUpdated = true
}
// not missing
continue next_repo
}
@@ -420,7 +448,7 @@ next_repo:
}
// 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", "--force", "--depth", "1", relpath, r.Name))
curBranch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, r.Name), "branch", "--show-current"))
if branch != curBranch {

View File

@@ -144,6 +144,7 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
return err
}
PrjGitOrg, _, _ := prset.Config.GetPrjGit()
for _, pr := range prset.PRs {
if prset.IsPrjGitPR(pr.PR) {
continue
@@ -198,7 +199,12 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
ref := fmt.Sprintf(common.PrPattern, org, repo, idx)
commitMsg := fmt.Sprintln("Add package", repo, "\n\nThis commit was autocreated by", GitAuthor, "\n\nreferencing PRs:\n", ref)
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "add", "-b", pr.PR.Base.Name, pr.PR.Base.Repo.SSHURL, repo)
relpath, err := common.RelativeRepositoryPath(PrjGitOrg, pr.PR.Base.Repo.CloneURL)
if err != nil {
common.LogError("Cannot calculate relative path for repository", pr.PR.Base.Repo.CloneURL, err)
return err
}
git.GitExecOrPanic(common.DefaultGitPrj, "submodule", "add", "-b", pr.PR.Base.Name, relpath, repo)
updateSubmoduleInPR(repo, prHead, git)
common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", commitMsg))
@@ -482,11 +488,11 @@ func (pr *PRProcessor) Process(req *models.PullRequest) error {
if _, ok := err.(*repository.RepoMergePullRequestConflict); !ok {
common.PanicOnError(err)
}
// } else {
// Gitea.AddComment(pr.PR, "Closing here because the associated Project PR has been closed.")
// Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{
// State: "closed",
// })
// } else {
// Gitea.AddComment(pr.PR, "Closing here because the associated Project PR has been closed.")
// Gitea.UpdatePullRequest(org, repo, idx, &models.EditPullRequestOption{
// State: "closed",
// })
}
}
}

View File

@@ -28,8 +28,10 @@ func TestPrjGitDescription(t *testing.T) {
Base: &models.PRBranchInfo{
Ref: "main",
Repo: &models.Repository{
Name: "pkg-a",
Owner: &models.User{UserName: "test-org"},
Name: "pkg-a",
Owner: &models.User{UserName: "test-org"},
CloneURL: "http://example.com/test-org/pkg-a.git",
SSHURL: "git@example.com:test-org/pkg-a.git",
},
},
},
@@ -202,8 +204,10 @@ func TestSetSubmodulesToMatchPRSet(t *testing.T) {
Base: &models.PRBranchInfo{
Ref: "main",
Repo: &models.Repository{
Name: "pkg-a",
Owner: &models.User{UserName: "test-org"},
Name: "pkg-a",
Owner: &models.User{UserName: "test-org"},
CloneURL: "http://example.com/test-org/pkg-a.git",
SSHURL: "git@example.com:test-org/pkg-a.git",
},
},
Head: &models.PRBranchInfo{
@@ -630,7 +634,12 @@ func TestCreatePRjGitPR_Integration(t *testing.T) {
PR: &models.PullRequest{
State: "open",
Base: &models.PRBranchInfo{
Repo: &models.Repository{Name: "pkg-a", Owner: &models.User{UserName: "test-org"}},
Repo: &models.Repository{
Name: "pkg-a",
Owner: &models.User{UserName: "test-org"},
CloneURL: "http://example.com/test-org/pkg-a.git",
SSHURL: "git@example.com:test-org/pkg-a.git",
},
},
Head: &models.PRBranchInfo{Sha: "pkg-sha"},
},
@@ -653,14 +662,23 @@ func TestCreatePRjGitPR_Integration(t *testing.T) {
Base: &models.PRBranchInfo{
Name: "main",
RepoID: 1,
Repo: &models.Repository{Name: "test-prj", Owner: &models.User{UserName: "test-org"}},
Repo: &models.Repository{
Name: "test-prj",
Owner: &models.User{UserName: "test-org"},
CloneURL: "http://example.com/test-org/test-prj.git",
SSHURL: "git@example.com:test-org/test-prj.git",
},
},
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()
gitea.EXPECT().GetRepository(gomock.Any(), gomock.Any()).Return(&models.Repository{
Owner: &models.User{UserName: "test-org"},
CloneURL: "http://example.com/test-org/test-prj.git",
SSHURL: "git@example.com:test-org/test-prj.git",
}, 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