diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 251ec7a80e1cd..72c5fb0d2e800 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -577,6 +577,19 @@ PATH = ;; (Go-Git only) Don't cache objects greater than this in memory. (Set to 0 to disable.) ;LARGE_OBJECT_THRESHOLD = 1048576 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; [git.submodule] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Submodule aliase +; MAP_NAME_1 = git@127.0.0.1 +; MAP_VALUE_1 = https://example.com +; MAP_NAME_2 = git@github.com/go-gitea/gitea +; MAP_VALUE_2 = https://gitea.com/gitea/gitea +; ... + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; [service] diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 611a7a887a9f9..9cbaf0dd2f683 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -840,6 +840,7 @@ NB: You must have `DISABLE_ROUTER_LOG` set to `false` for this option to take ef - `VERBOSE_PUSH_DELAY`: **5s**: Only print verbose information if push takes longer than this delay. - `LARGE_OBJECT_THRESHOLD`: **1048576**: (Go-Git only), don't cache objects greater than this in memory. (Set to 0 to disable.) ## Git - Timeout settings (`git.timeout`) + - `DEFAUlT`: **360**: Git operations default timeout seconds. - `MIGRATE`: **600**: Migrate external repositories timeout seconds. - `MIRROR`: **300**: Mirror external repositories timeout seconds. @@ -847,6 +848,12 @@ NB: You must have `DISABLE_ROUTER_LOG` set to `false` for this option to take ef - `PULL`: **300**: Git pull from internal repositories timeout seconds. - `GC`: **60**: Git repository GC timeout seconds. +## Git - Submodule alias settings (`git.submodule`) + +- List of `MAP_NAME_` and `MAP_VALUE_` pairs that map submodule urls or partial urls to web paths. For example: + - `MAP_NAME_1 = git@127.0.0.1` & `MAP_VALUE_1 = https://external` would convert any submodules referring to `git@127.0.0.1` to `https://external` + - `MAP_NAME_A = git@127.0.0.1:owner/repo` & `MAP_VALUE_A = https://external/externalowner/externalrepo` + ## Metrics (`metrics`) - `ENABLED`: **false**: Enables /metrics endpoint for prometheus. diff --git a/modules/git/submodule.go b/modules/git/submodule.go index 231827f1e989a..88f0ad48d7988 100644 --- a/modules/git/submodule.go +++ b/modules/git/submodule.go @@ -12,6 +12,8 @@ import ( "path" "regexp" "strings" + + "code.gitea.io/gitea/modules/setting" ) var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`) @@ -44,8 +46,16 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { return "" } + if alias, ok := setting.Git.SubModuleMap[refURL]; ok { + return alias + } + refURI := strings.TrimSuffix(refURL, ".git") + if alias, ok := setting.Git.SubModuleMap[refURI]; ok { + return alias + } + prefixURL, _ := url.Parse(urlPrefix) urlPrefixHostname, _, err := net.SplitHostPort(prefixURL.Host) if err != nil { @@ -76,6 +86,10 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { pth = "/" + pth } + if alias, ok := setting.Git.SubModuleMap[m[1]+refHostname]; ok { + return alias + pth + } + if urlPrefixHostname == refHostname || refHostname == sshDomain { return urlPrefix + path.Clean(path.Join("/", pth)) } @@ -95,6 +109,33 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { supportedSchemes := []string{"http", "https", "git", "ssh", "git+ssh"} + if len(setting.Git.SubModuleMap) > 0 && ref.Scheme != "" { + if ref.Scheme == "ssh" { + if len(ref.User.Username()) > 0 { + if alias, ok := setting.Git.SubModuleMap[fmt.Sprintf("%v@%s:%s", ref.User, ref.Host, ref.Path[1:])]; ok { + return alias + } + if alias, ok := setting.Git.SubModuleMap[fmt.Sprintf("%v@%s", ref.User, ref.Host)]; ok { + return alias + ref.Path + } + } else if alias, ok := setting.Git.SubModuleMap[ref.Host+":"+ref.Path[1:]]; ok { + return alias + } else if alias, ok := setting.Git.SubModuleMap[ref.Host]; ok { + return alias + ref.Path + } + } + left := refURI + right := "" + for idx := strings.LastIndex(left, "/"); idx > len(ref.Scheme)+3; { + right = left[idx:] + right + left = left[:idx] + if alias, ok := setting.Git.SubModuleMap[left]; ok { + return alias + right + } + idx = strings.LastIndex(left, "/") + } + } + for _, scheme := range supportedSchemes { if ref.Scheme == scheme { if ref.Scheme == "http" || ref.Scheme == "https" { diff --git a/modules/git/submodule_test.go b/modules/git/submodule_test.go index ff8dc579f6d51..b5e30efe355c4 100644 --- a/modules/git/submodule_test.go +++ b/modules/git/submodule_test.go @@ -7,37 +7,62 @@ package git import ( "testing" + "code.gitea.io/gitea/modules/setting" + "github.com/stretchr/testify/assert" ) func TestGetRefURL(t *testing.T) { var kases = []struct { - refURL string - prefixURL string - parentPath string - SSHDomain string - expect string + refURL string + prefixURL string + parentPath string + SSHDomain string + expect string + subModuleMap map[string]string }{ - {"git://github.com/user1/repo1", "/", "user1/repo2", "", "http://github.com/user1/repo1"}, - {"https://localhost/user1/repo1.git", "/", "user1/repo2", "", "https://localhost/user1/repo1"}, - {"http://localhost/user1/repo1.git", "/", "owner/reponame", "", "http://localhost/user1/repo1"}, - {"git@github.com:user1/repo1.git", "/", "owner/reponame", "", "http://github.com/user1/repo1"}, - {"ssh://git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/zefie/lge_g6_kernel_scripts"}, - {"git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/2222/zefie/lge_g6_kernel_scripts"}, - {"git@try.gitea.io:go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"}, - {"ssh://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"}, - {"git://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"}, - {"ssh://git@127.0.0.1:9999/go-gitea/gitea", "https://127.0.0.1:3000/", "go-gitea/sdk", "", "https://127.0.0.1:3000/go-gitea/gitea"}, - {"https://gitea.com:3000/user1/repo1.git", "https://127.0.0.1:3000/", "user/repo2", "", "https://gitea.com:3000/user1/repo1"}, - {"https://example.gitea.com/gitea/user1/repo1.git", "https://example.gitea.com/gitea/", "", "user/repo2", "https://example.gitea.com/gitea/user1/repo1"}, - {"https://username:password@github.com/username/repository.git", "/", "username/repository2", "", "https://username:password@github.com/username/repository"}, - {"somethingbad", "https://127.0.0.1:3000/go-gitea/gitea", "/", "", ""}, - {"git@localhost:user/repo", "https://localhost/", "user2/repo1", "", "https://localhost/user/repo"}, - {"../path/to/repo.git/", "https://localhost/", "user/repo2", "", "https://localhost/user/path/to/repo.git"}, - {"ssh://git@ssh.gitea.io:2222/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "ssh.gitea.io", "https://try.gitea.io/go-gitea/gitea"}, + {"git://github.com/user1/repo1", "/", "user1/repo2", "", "http://github.com/user1/repo1", map[string]string{}}, + {"https://localhost/user1/repo1.git", "/", "user1/repo2", "", "https://localhost/user1/repo1", map[string]string{}}, + {"http://localhost/user1/repo1.git", "/", "owner/reponame", "", "http://localhost/user1/repo1", map[string]string{}}, + {"git@github.com:user1/repo1.git", "/", "owner/reponame", "", "http://github.com/user1/repo1", map[string]string{}}, + {"ssh://git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/zefie/lge_g6_kernel_scripts", map[string]string{}}, + {"git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/2222/zefie/lge_g6_kernel_scripts", map[string]string{}}, + {"git@try.gitea.io:go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea", map[string]string{}}, + {"ssh://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea", map[string]string{}}, + {"git://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea", map[string]string{}}, + {"ssh://git@127.0.0.1:9999/go-gitea/gitea", "https://127.0.0.1:3000/", "go-gitea/sdk", "", "https://127.0.0.1:3000/go-gitea/gitea", map[string]string{}}, + {"https://gitea.com:3000/user1/repo1.git", "https://127.0.0.1:3000/", "user/repo2", "", "https://gitea.com:3000/user1/repo1", map[string]string{}}, + {"https://example.gitea.com/gitea/user1/repo1.git", "https://example.gitea.com/gitea/", "", "user/repo2", "https://example.gitea.com/gitea/user1/repo1", map[string]string{}}, + {"https://username:password@github.com/username/repository.git", "/", "username/repository2", "", "https://username:password@github.com/username/repository", map[string]string{}}, + {"somethingbad", "https://127.0.0.1:3000/go-gitea/gitea", "/", "", "", map[string]string{}}, + {"git@localhost:user/repo", "https://localhost/", "user2/repo1", "", "https://localhost/user/repo", map[string]string{}}, + {"../path/to/repo.git/", "https://localhost/", "user/repo2", "", "https://localhost/user/path/to/repo.git", map[string]string{}}, + {"ssh://git@ssh.gitea.io:2222/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "ssh.gitea.io", "https://try.gitea.io/go-gitea/gitea", map[string]string{}}, + {"ssh://git@ssh.gitea.io:2222/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "try.gitea.io", "https://try.gitea.io/go-gitea/gitea", map[string]string{ + "ssh://git@ssh.gitea.io:2222": "https://try.gitea.io", + }}, + {"git@ssh.gitea.io:go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "try.gitea.io", "https://try.gitea.io/go-gitea/gitea", map[string]string{ + "git@ssh.gitea.io": "https://try.gitea.io", + "ssh://git@ssh.gitea.io:2222": "Wrong", + }}, + {"ssh://git@ssh.gitea.io/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "try.gitea.io", "https://try.gitea.io/go-gitea/gitea", map[string]string{ + "ssh://git@ssh.gitea.io": "https://try.gitea.io", + "ssh://git@ssh.gitea.io:2222": "Wrong", + }}, + {"ssh://git@ssh.gitea.io/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "try.gitea.io", "https://try.gitea.io/go-gitea/gitea", map[string]string{ + "git@ssh.gitea.io": "https://try.gitea.io", + }}, + {"ssh://git@ssh.gitea.io/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "try.gitea.io", "https://try.gitea.io/go-gitea/gitea", map[string]string{ + "git@ssh.gitea.io": "https://try.gitea.io", + }}, + {"ssh://git@ssh.gitea.io/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "try.gitea.io", "https://try.gitea.io/go-gitea/gitea", map[string]string{ + "git@ssh.gitea.io:go-gitea/gitea": "https://try.gitea.io/go-gitea/gitea", + }}, } - + orig := setting.Git.SubModuleMap for _, kase := range kases { + setting.Git.SubModuleMap = kase.subModuleMap assert.EqualValues(t, kase.expect, getRefURL(kase.refURL, kase.prefixURL, kase.parentPath, kase.SSHDomain)) } + setting.Git.SubModuleMap = orig } diff --git a/modules/setting/git.go b/modules/setting/git.go index aa838a8d641a7..1198e019edebf 100644 --- a/modules/setting/git.go +++ b/modules/setting/git.go @@ -5,6 +5,7 @@ package setting import ( + "strings" "time" "code.gitea.io/gitea/modules/log" @@ -34,6 +35,7 @@ var ( Pull int GC int `ini:"GC"` } `ini:"git.timeout"` + SubModuleMap map[string]string `ini:"-"` }{ DisableDiffHighlight: false, MaxGitDiffLines: 1000, @@ -62,6 +64,7 @@ var ( Pull: 300, GC: 60, }, + SubModuleMap: map[string]string{}, } ) @@ -69,4 +72,21 @@ func newGit() { if err := Cfg.Section("git").MapTo(&Git); err != nil { log.Fatal("Failed to map Git settings: %v", err) } + submoduleSection := Cfg.Section("git.submodule") + for key, nameValue := range submoduleSection.KeysHash() { + if !strings.HasPrefix(key, "MAP_NAME_") { + continue + } + if nameValue == "" { + continue + } + if !submoduleSection.HasKey("MAP_VALUE_" + key[9:]) { + log.Error("Missing value for submodule map: %s", key) + continue + } + valueValue := submoduleSection.Key("MAP_VALUE_" + key[9:]).MustString("") + if valueValue != "" { + Git.SubModuleMap[nameValue] = valueValue + } + } }