From 56c0832f0499f066b8bbcb86cbc1aac7e8feea2871cdcd81488a92f615983f19 Mon Sep 17 00:00:00 2001 From: Adam Majer Date: Mon, 9 Feb 2026 16:09:00 +0100 Subject: [PATCH 1/2] common: Add relative path repository resolution helper --- common/utils.go | 57 +++++++++++++++++++ common/utils_test.go | 127 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 178 insertions(+), 6 deletions(-) diff --git a/common/utils.go b/common/utils.go index c907c8a..36d51a5 100644 --- a/common/utils.go +++ b/common/utils.go @@ -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 diff --git a/common/utils_test.go b/common/utils_test.go index d2286c8..786e860 100644 --- a/common/utils_test.go +++ b/common/utils_test.go @@ -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 }{ -- 2.51.1 From 07a5fbe4c313298e3a105e898deb1075d01728c908f87e0e71a5569ceae64d65 Mon Sep 17 00:00:00 2001 From: Adam Majer Date: Mon, 9 Feb 2026 16:39:01 +0100 Subject: [PATCH 2/2] direct: use relative paths when adding repositories --- workflow-direct/main.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/workflow-direct/main.go b/workflow-direct/main.go index b6f1dfc..0f89788 100644 --- a/workflow-direct/main.go +++ b/workflow-direct/main.go @@ -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 { -- 2.51.1