2 Commits

Author SHA256 Message Date
07a5fbe4c3 direct: use relative paths when adding repositories
All checks were successful
go-generate-check / go-generate-check (pull_request) Successful in 8s
2026-02-09 16:39:01 +01:00
56c0832f04 common: Add relative path repository resolution helper
All checks were successful
go-generate-check / go-generate-check (pull_request) Successful in 21s
2026-02-09 16:09:00 +01:00
3 changed files with 184 additions and 8 deletions

View File

@@ -43,6 +43,63 @@ 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, packagegit string) (string, error) {
if len(prjgit) == 0 || len(packagegit) == 0 {
return "", InvalidUrlError
}
prjgiturl, err := url.Parse(prjgit)
if err != nil {
return "", err
}
if !prjgiturl.IsAbs() {
return "", AbsoluteUrlError
}
if len(prjgiturl.RawQuery) != 0 {
return "", AbsoluteUrlWithQuery
}
pkggiturl, err := url.Parse(packagegit)
if err != nil {
return "", err
}
if !pkggiturl.IsAbs() {
return "", AbsoluteUrlError
}
if len(pkggiturl.RawQuery) != 0 {
return "", AbsoluteUrlWithQuery
}
if pkggiturl.Hostname() != prjgiturl.Hostname() {
return "", HostsNotEqualError
}
prjgitpath := SplitStringNoEmpty(prjgiturl.Path, "/")
pkggitpath := SplitStringNoEmpty(pkggiturl.Path, "/")
if len(prjgitpath) != 2 || len(pkggitpath) != 2 {
return "", InvalidPath
}
prjgitpath[1] = strings.TrimRight(prjgitpath[1], ".git")
pkggitpath[1] = strings.TrimRight(pkggitpath[1], ".git")
if prjgitpath[0] == pkggitpath[0] {
if prjgitpath[1] == pkggitpath[1] {
return "", InvalidPath
}
return "../" + pkggitpath[1], nil
}
return "../../" + pkggitpath[0] + "/" + pkggitpath[1], nil
}
func HasSpace(s string) bool {
return strings.IndexFunc(s, unicode.IsSpace) >= 0

View File

@@ -7,6 +7,121 @@ import (
"src.opensuse.org/autogits/common"
)
func TestRelativeRepositoryPath(t *testing.T) {
tests := []struct {
name string
gitprj, repo string
hasError bool
relative string
}{
{
name: "Invalid repository URLs",
hasError: true,
},
{
name: "Invalid repository URLs",
gitprj: "http://test",
hasError: true,
},
{
name: "Not absolute urls",
gitprj: "foo httptest",
repo: "http://test",
hasError: true,
},
{
name: "Not aboslute urls",
gitprj: "http://test",
repo: "/test",
hasError: true,
},
{
name: "Repos not on the same server",
gitprj: "https://host1/path1/path2",
repo: "https://host2/path1/path2",
hasError: true,
},
{
name: "Repos with query parameters",
gitprj: "https://host1/path1/path2?query=foo",
repo: "https://host1/path1/path3",
hasError: true,
},
{
name: "Repos with query parameters",
gitprj: "https://host1/path1/path2",
repo: "https://host1/path1/path3?query=foo",
hasError: true,
},
{
name: "Repos are not same repo",
gitprj: "https://host1/path1/path2.git",
repo: "https://host1/path1/path2",
hasError: true,
},
{
name: "Repos in same org",
gitprj: "https://host1/path1/path2.git",
repo: "https://host1/path1/path3",
relative: "../path3",
},
{
name: "Repos in same org",
gitprj: "https://host1/path1/path2.git",
repo: "https://host1/path1/path3.git",
relative: "../path3",
},
{
name: "Repos in different org",
gitprj: "https://host1/path1/path2.git",
repo: "https://host1/path2/path3.git",
relative: "../../path2/path3",
},
{
name: "Too long paths",
gitprj: "https://host1/path1/path2.git",
repo: "https://host1/path2/path3/path3.git",
hasError: true,
},
{
name: "Too long paths",
gitprj: "https://host1/path1/path2/path2.git",
repo: "https://host1/path2/path3.git",
hasError: true,
},
{
name: "SSH repos not supported",
gitprj: "https://host1/path1/path2.git",
repo: "gitea@src.opensuse.org:path1/path3.git",
hasError: true,
},
{
name: "SSH repos not supported",
gitprj: "gitea@src.opensuse.org:path1/path3.git",
repo: "https://host1/path1/path2.git",
hasError: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
r, err := common.RelativeRepositoryPath(test.gitprj, 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
@@ -241,7 +356,7 @@ func TestNewPackageIssueParsing(t *testing.T) {
},
},
{
name: "Default branch and junk lines and approval for maintainership",
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 }{
@@ -251,7 +366,7 @@ func TestNewPackageIssueParsing(t *testing.T) {
},
},
{
name: "Default branch and junk lines and no maintainership",
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 }{
@@ -260,7 +375,7 @@ func TestNewPackageIssueParsing(t *testing.T) {
},
},
{
name: "3 repos with comments and maintainership",
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 }{
@@ -272,11 +387,11 @@ func TestNewPackageIssueParsing(t *testing.T) {
},
},
{
name: "Invalid repos with spaces",
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",
name: "Valid repos with spaces",
input: " org / repo # branch",
issues: &common.NewRepos{
Repos: []struct{ Organization, Repository, Branch, PackageName string }{
@@ -285,7 +400,7 @@ func TestNewPackageIssueParsing(t *testing.T) {
},
},
{
name: "Package name is not repo name",
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 }{

View File

@@ -123,7 +123,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(prjGitRepo.CloneURL, 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"))
@@ -420,7 +422,9 @@ next_repo:
}
// add repository to git project
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", r.CloneURL, r.Name))
relpath, err := common.RelativeRepositoryPath(repo.CloneURL, r.CloneURL)
common.PanicOnError(err)
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 {