aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-04-05 20:21:32 +0800
committerRunxi Yu <me@runxiyu.org>2025-04-05 20:21:32 +0800
commit71ab9b7f14118f02dd18cd733bd4e0ad19ece590 (patch)
tree0303cbee651a4e1cee62a348d25066b9543f4425
parentgit2d: Remove UTF-8 checks (diff)
downloadforge-71ab9b7f14118f02dd18cd733bd4e0ad19ece590.tar.gz
forge-71ab9b7f14118f02dd18cd733bd4e0ad19ece590.tar.zst
forge-71ab9b7f14118f02dd18cd733bd4e0ad19ece590.zip
config shall no longer be a global variable
-rw-r--r--.golangci.yaml1
-rw-r--r--acl.go2
-rw-r--r--config.go12
-rw-r--r--database.go2
-rw-r--r--fedauth.go2
-rw-r--r--git2d_deploy.go4
-rw-r--r--git_hooks_deploy.go8
-rw-r--r--git_hooks_handle_linux.go10
-rw-r--r--git_hooks_handle_other.go10
-rw-r--r--git_init.go4
-rw-r--r--http_handle_branches.go6
-rw-r--r--http_handle_group_index.go6
-rw-r--r--http_handle_index.go4
-rw-r--r--http_handle_login.go4
-rw-r--r--http_handle_repo_index.go6
-rw-r--r--http_handle_repo_raw.go6
-rw-r--r--http_handle_repo_tree.go6
-rw-r--r--http_handle_repo_upload_pack.go4
-rw-r--r--http_server.go28
-rw-r--r--irc.go20
-rw-r--r--lmtp_server.go21
-rw-r--r--main.go78
-rw-r--r--remote_url.go8
-rw-r--r--server.go5
-rw-r--r--ssh_handle_receive_pack.go8
-rw-r--r--ssh_handle_upload_pack.go6
-rw-r--r--ssh_server.go10
-rw-r--r--ssh_utils.go4
28 files changed, 145 insertions, 140 deletions
diff --git a/.golangci.yaml b/.golangci.yaml
index 73eff0c..1c8c972 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -5,7 +5,6 @@ linters:
- depguard
- err113 # dynamically defined errors are fine for our purposes
- forcetypeassert # type assertion failures are usually programming errors
- - gochecknoglobals # doesn't matter since this isn't a library
- gochecknoinits # we use inits sparingly for good reasons
- godox # they're just used as markers for where needs improvements
- ireturn # doesn't work well with how we use generics
diff --git a/acl.go b/acl.go
index 5f242d5..44cd04b 100644
--- a/acl.go
+++ b/acl.go
@@ -13,7 +13,7 @@ import (
// given repo and a provided ssh public key.
//
// TODO: Revamp.
-func getRepoInfo(ctx context.Context, groupPath []string, repoName, sshPubkey string) (repoID int, fsPath string, access bool, contribReq, userType string, userID int, err error) {
+func (s *server) getRepoInfo(ctx context.Context, groupPath []string, repoName, sshPubkey string) (repoID int, fsPath string, access bool, contribReq, userType string, userID int, err error) {
err = database.QueryRow(ctx, `
WITH RECURSIVE group_path_cte AS (
-- Start: match the first name in the path where parent_group IS NULL
diff --git a/config.go b/config.go
index 6220d6f..1bbc3a1 100644
--- a/config.go
+++ b/config.go
@@ -16,7 +16,7 @@ import (
// config holds the global configuration used by this instance. There is
// currently no synchronization mechanism, so it must not be modified after
// request handlers are spawned.
-var config struct {
+type Config struct {
HTTP struct {
Net string `scfg:"net"`
Addr string `scfg:"addr"`
@@ -76,7 +76,7 @@ var config struct {
// configuration patterns, but silently ignores fields in the [config] struct
// that is not present in the user's configuration file. We would prefer the
// exact opposite behavior.
-func loadConfig(path string) (err error) {
+func (s *server) loadConfig(path string) (err error) {
var configFile *os.File
if configFile, err = os.Open(path); err != nil {
return err
@@ -84,19 +84,19 @@ func loadConfig(path string) (err error) {
defer configFile.Close()
decoder := scfg.NewDecoder(bufio.NewReader(configFile))
- if err = decoder.Decode(&config); err != nil {
+ if err = decoder.Decode(&s.config); err != nil {
return err
}
- if config.DB.Type != "postgres" {
+ if s.config.DB.Type != "postgres" {
return errors.New("unsupported database type")
}
- if database, err = pgxpool.New(context.Background(), config.DB.Conn); err != nil {
+ if database, err = pgxpool.New(context.Background(), s.config.DB.Conn); err != nil {
return err
}
- globalData["forge_title"] = config.General.Title
+ globalData["forge_title"] = s.config.General.Title
return nil
}
diff --git a/database.go b/database.go
index a3f5ab8..18e753f 100644
--- a/database.go
+++ b/database.go
@@ -24,7 +24,7 @@ var database *pgxpool.Pool
// queryNameDesc is a helper function that executes a query and returns a
// list of nameDesc results. The query must return two string arguments, i.e. a
// name and a description.
-func queryNameDesc(ctx context.Context, query string, args ...any) (result []nameDesc, err error) {
+func (s *server) queryNameDesc(ctx context.Context, query string, args ...any) (result []nameDesc, err error) {
var rows pgx.Rows
if rows, err = database.Query(ctx, query, args...); err != nil {
diff --git a/fedauth.go b/fedauth.go
index 808fba5..46290e5 100644
--- a/fedauth.go
+++ b/fedauth.go
@@ -17,7 +17,7 @@ import (
// fedauth checks whether a user's SSH public key matches the remote username
// they claim to have on the service. If so, the association is recorded.
-func fedauth(ctx context.Context, userID int, service, remoteUsername, pubkey string) (bool, error) {
+func (s *server) fedauth(ctx context.Context, userID int, service, remoteUsername, pubkey string) (bool, error) {
var err error
matched := false
diff --git a/git2d_deploy.go b/git2d_deploy.go
index 09332a8..f3a4cc9 100644
--- a/git2d_deploy.go
+++ b/git2d_deploy.go
@@ -9,7 +9,7 @@ import (
"os"
)
-func deployGit2D() (err error) {
+func (s *server) deployGit2D() (err error) {
var srcFD fs.File
var dstFD *os.File
@@ -18,7 +18,7 @@ func deployGit2D() (err error) {
}
defer srcFD.Close()
- if dstFD, err = os.OpenFile(config.Git.DaemonPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755); err != nil {
+ if dstFD, err = os.OpenFile(s.config.Git.DaemonPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755); err != nil {
return err
}
defer dstFD.Close()
diff --git a/git_hooks_deploy.go b/git_hooks_deploy.go
index c9039fe..ea11d0c 100644
--- a/git_hooks_deploy.go
+++ b/git_hooks_deploy.go
@@ -14,7 +14,7 @@ import (
// deployHooks deploys the git hooks client to the filesystem. The git hooks
// client is expected to be embedded in resourcesFS and must be pre-compiled
// during the build process; see the Makefile.
-func deployHooks() (err error) {
+func (s *server) deployHooks() (err error) {
err = func() (err error) {
var srcFD fs.File
var dstFD *os.File
@@ -24,7 +24,7 @@ func deployHooks() (err error) {
}
defer srcFD.Close()
- if dstFD, err = os.OpenFile(filepath.Join(config.Hooks.Execs, "hookc"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755); err != nil {
+ if dstFD, err = os.OpenFile(filepath.Join(s.config.Hooks.Execs, "hookc"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755); err != nil {
return err
}
defer dstFD.Close()
@@ -41,14 +41,14 @@ func deployHooks() (err error) {
// Go's embed filesystems do not store permissions; but in any case,
// they would need to be 0o755:
- if err = os.Chmod(filepath.Join(config.Hooks.Execs, "hookc"), 0o755); err != nil {
+ if err = os.Chmod(filepath.Join(s.config.Hooks.Execs, "hookc"), 0o755); err != nil {
return err
}
for _, hookName := range []string{
"pre-receive",
} {
- if err = os.Symlink(filepath.Join(config.Hooks.Execs, "hookc"), filepath.Join(config.Hooks.Execs, hookName)); err != nil {
+ if err = os.Symlink(filepath.Join(s.config.Hooks.Execs, "hookc"), filepath.Join(s.config.Hooks.Execs, hookName)); err != nil {
if !errors.Is(err, fs.ErrExist) {
return err
}
diff --git a/git_hooks_handle_linux.go b/git_hooks_handle_linux.go
index 8c8d34b..37afba1 100644
--- a/git_hooks_handle_linux.go
+++ b/git_hooks_handle_linux.go
@@ -34,7 +34,7 @@ var (
// hooksHandler handles a connection from hookc via the
// unix socket.
-func hooksHandler(conn net.Conn) {
+func (s *server) hooksHandler(conn net.Conn) {
var ctx context.Context
var cancel context.CancelFunc
var ucred *syscall.Ucred
@@ -187,7 +187,7 @@ func hooksHandler(conn net.Conn) {
return 1
}
- ok, err := fedauth(ctx, packPass.userID, service, username, packPass.pubkey)
+ ok, err := s.fedauth(ctx, packPass.userID, service, username, packPass.pubkey)
if err != nil {
writeRedError(sshStderr, "Failed to verify federated user identifier %#v: %v", fedUserID, err)
return 1
@@ -247,7 +247,7 @@ func hooksHandler(conn net.Conn) {
writeRedError(sshStderr, "Error creating merge request: %v", err)
return 1
}
- mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRLocalID)
+ mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", s.genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRLocalID)
fmt.Fprintln(sshStderr, ansiec.Blue+"Created merge request at", mergeRequestWebURL+ansiec.Reset)
select {
@@ -342,13 +342,13 @@ func hooksHandler(conn net.Conn) {
// treats incoming connections as those from git hook handlers by spawning
// sessions. The listener must be a SOCK_STREAM UNIX domain socket. The
// function itself blocks.
-func serveGitHooks(listener net.Listener) error {
+func (s *server) serveGitHooks(listener net.Listener) error {
for {
conn, err := listener.Accept()
if err != nil {
return err
}
- go hooksHandler(conn)
+ go s.hooksHandler(conn)
}
}
diff --git a/git_hooks_handle_other.go b/git_hooks_handle_other.go
index fdeca83..6d5b08d 100644
--- a/git_hooks_handle_other.go
+++ b/git_hooks_handle_other.go
@@ -29,7 +29,7 @@ var errGetFD = errors.New("unable to get file descriptor")
// hooksHandler handles a connection from hookc via the
// unix socket.
-func hooksHandler(conn net.Conn) {
+func (s *server) hooksHandler(conn net.Conn) {
var ctx context.Context
var cancel context.CancelFunc
var err error
@@ -165,7 +165,7 @@ func hooksHandler(conn net.Conn) {
return 1
}
- ok, err := fedauth(ctx, packPass.userID, service, username, packPass.pubkey)
+ ok, err := s.fedauth(ctx, packPass.userID, service, username, packPass.pubkey)
if err != nil {
writeRedError(sshStderr, "Failed to verify federated user identifier %#v: %v", fedUserID, err)
return 1
@@ -225,7 +225,7 @@ func hooksHandler(conn net.Conn) {
writeRedError(sshStderr, "Error creating merge request: %v", err)
return 1
}
- mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRLocalID)
+ mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", s.genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRLocalID)
fmt.Fprintln(sshStderr, ansiec.Blue+"Created merge request at", mergeRequestWebURL+ansiec.Reset)
select {
@@ -320,13 +320,13 @@ func hooksHandler(conn net.Conn) {
// treats incoming connections as those from git hook handlers by spawning
// sessions. The listener must be a SOCK_STREAM UNIX domain socket. The
// function itself blocks.
-func serveGitHooks(listener net.Listener) error {
+func (s *server) serveGitHooks(listener net.Listener) error {
for {
conn, err := listener.Accept()
if err != nil {
return err
}
- go hooksHandler(conn)
+ go s.hooksHandler(conn)
}
}
diff --git a/git_init.go b/git_init.go
index f1a283e..1800c5a 100644
--- a/git_init.go
+++ b/git_init.go
@@ -11,7 +11,7 @@ import (
// gitInit initializes a bare git repository with the forge-deployed hooks
// directory as the hooksPath.
-func gitInit(repoPath string) (err error) {
+func (s *server) gitInit(repoPath string) (err error) {
var repo *git.Repository
var gitConf *gitConfig.Config
@@ -23,7 +23,7 @@ func gitInit(repoPath string) (err error) {
return err
}
- gitConf.Raw.SetOption("core", gitFmtConfig.NoSubsection, "hooksPath", config.Hooks.Execs)
+ gitConf.Raw.SetOption("core", gitFmtConfig.NoSubsection, "hooksPath", s.config.Hooks.Execs)
gitConf.Raw.SetOption("receive", gitFmtConfig.NoSubsection, "advertisePushOptions", "true")
if err = repo.SetConfig(gitConf); err != nil {
diff --git a/http_handle_branches.go b/http_handle_branches.go
index 48ba5ab..d386b82 100644
--- a/http_handle_branches.go
+++ b/http_handle_branches.go
@@ -13,7 +13,7 @@ import (
)
// httpHandleRepoBranches provides the branches page in repos.
-func httpHandleRepoBranches(writer http.ResponseWriter, _ *http.Request, params map[string]any) {
+func (s *server) httpHandleRepoBranches(writer http.ResponseWriter, _ *http.Request, params map[string]any) {
var repo *git.Repository
var repoName string
var groupPath []string
@@ -37,8 +37,8 @@ func httpHandleRepoBranches(writer http.ResponseWriter, _ *http.Request, params
}
params["branches"] = branches
- params["http_clone_url"] = genHTTPRemoteURL(groupPath, repoName)
- params["ssh_clone_url"] = genSSHRemoteURL(groupPath, repoName)
+ params["http_clone_url"] = s.genHTTPRemoteURL(groupPath, repoName)
+ params["ssh_clone_url"] = s.genSSHRemoteURL(groupPath, repoName)
params["notes"] = notes
renderTemplate(writer, "repo_branches", params)
diff --git a/http_handle_group_index.go b/http_handle_group_index.go
index 568a38e..16120a8 100644
--- a/http_handle_group_index.go
+++ b/http_handle_group_index.go
@@ -17,7 +17,7 @@ import (
// httpHandleGroupIndex provides index pages for groups, which includes a list
// of its subgroups and repos, as well as a form for group maintainers to
// create repos.
-func httpHandleGroupIndex(writer http.ResponseWriter, request *http.Request, params map[string]any) {
+func (s *server) httpHandleGroupIndex(writer http.ResponseWriter, request *http.Request, params map[string]any) {
var groupPath []string
var repos []nameDesc
var subgroups []nameDesc
@@ -111,7 +111,7 @@ func httpHandleGroupIndex(writer http.ResponseWriter, request *http.Request, par
return
}
- filePath := filepath.Join(config.Git.RepoDir, strconv.Itoa(newRepoID)+".git")
+ filePath := filepath.Join(s.config.Git.RepoDir, strconv.Itoa(newRepoID)+".git")
_, err = database.Exec(
request.Context(),
@@ -126,7 +126,7 @@ func httpHandleGroupIndex(writer http.ResponseWriter, request *http.Request, par
return
}
- if err = gitInit(filePath); err != nil {
+ if err = s.gitInit(filePath); err != nil {
errorPage500(writer, params, "Error initializing repo: "+err.Error())
return
}
diff --git a/http_handle_index.go b/http_handle_index.go
index 5d2dc3e..755e7c4 100644
--- a/http_handle_index.go
+++ b/http_handle_index.go
@@ -12,11 +12,11 @@ import (
// httpHandleIndex provides the main index page which includes a list of groups
// and some global information such as SSH keys.
-func httpHandleIndex(writer http.ResponseWriter, request *http.Request, params map[string]any) {
+func (s *server) httpHandleIndex(writer http.ResponseWriter, request *http.Request, params map[string]any) {
var err error
var groups []nameDesc
- groups, err = queryNameDesc(request.Context(), "SELECT name, COALESCE(description, '') FROM groups WHERE parent_group IS NULL")
+ groups, err = s.queryNameDesc(request.Context(), "SELECT name, COALESCE(description, '') FROM groups WHERE parent_group IS NULL")
if err != nil {
errorPage500(writer, params, "Error querying groups: "+err.Error())
return
diff --git a/http_handle_login.go b/http_handle_login.go
index 56c0a82..ea1dbae 100644
--- a/http_handle_login.go
+++ b/http_handle_login.go
@@ -16,7 +16,7 @@ import (
)
// httpHandleLogin provides the login page for local users.
-func httpHandleLogin(writer http.ResponseWriter, request *http.Request, params map[string]any) {
+func (s *server) httpHandleLogin(writer http.ResponseWriter, request *http.Request, params map[string]any) {
var username, password string
var userID int
var passwordHash string
@@ -71,7 +71,7 @@ func httpHandleLogin(writer http.ResponseWriter, request *http.Request, params m
}
now = time.Now()
- expiry = now.Add(time.Duration(config.HTTP.CookieExpiry) * time.Second)
+ expiry = now.Add(time.Duration(s.config.HTTP.CookieExpiry) * time.Second)
cookie = http.Cookie{
Name: "session",
diff --git a/http_handle_repo_index.go b/http_handle_repo_index.go
index ef1b76e..c253fa9 100644
--- a/http_handle_repo_index.go
+++ b/http_handle_repo_index.go
@@ -20,18 +20,18 @@ type commitDisplay struct {
}
// httpHandleRepoIndex provides the front page of a repo using git2d.
-func httpHandleRepoIndex(w http.ResponseWriter, req *http.Request, params map[string]any) {
+func (s *server) httpHandleRepoIndex(w http.ResponseWriter, req *http.Request, params map[string]any) {
repoName := params["repo_name"].(string)
groupPath := params["group_path"].([]string)
- _, repoPath, _, _, _, _, _ := getRepoInfo(req.Context(), groupPath, repoName, "") // TODO: Don't use getRepoInfo
+ _, repoPath, _, _, _, _, _ := s.getRepoInfo(req.Context(), groupPath, repoName, "") // TODO: Don't use getRepoInfo
var notes []string
if strings.Contains(repoName, "\n") || sliceContainsNewlines(groupPath) {
notes = append(notes, "Path contains newlines; HTTP Git access impossible")
}
- client, err := git2c.NewClient(config.Git.Socket)
+ client, err := git2c.NewClient(s.config.Git.Socket)
if err != nil {
errorPage500(w, params, err.Error())
return
diff --git a/http_handle_repo_raw.go b/http_handle_repo_raw.go
index 3a4e152..570030f 100644
--- a/http_handle_repo_raw.go
+++ b/http_handle_repo_raw.go
@@ -15,16 +15,16 @@ import (
// httpHandleRepoRaw serves raw files, or directory listings that point to raw
// files.
-func httpHandleRepoRaw(writer http.ResponseWriter, request *http.Request, params map[string]any) {
+func (s *server) httpHandleRepoRaw(writer http.ResponseWriter, request *http.Request, params map[string]any) {
repoName := params["repo_name"].(string)
groupPath := params["group_path"].([]string)
rawPathSpec := params["rest"].(string)
pathSpec := strings.TrimSuffix(rawPathSpec, "/")
params["path_spec"] = pathSpec
- _, repoPath, _, _, _, _, _ := getRepoInfo(request.Context(), groupPath, repoName, "")
+ _, repoPath, _, _, _, _, _ := s.getRepoInfo(request.Context(), groupPath, repoName, "")
- client, err := git2c.NewClient(config.Git.Socket)
+ client, err := git2c.NewClient(s.config.Git.Socket)
if err != nil {
errorPage500(writer, params, err.Error())
return
diff --git a/http_handle_repo_tree.go b/http_handle_repo_tree.go
index 9cdd9cd..7af6e3e 100644
--- a/http_handle_repo_tree.go
+++ b/http_handle_repo_tree.go
@@ -16,16 +16,16 @@ import (
// individual files, and provides directory views that link to these files.
//
// TODO: Do not highlight files that are too large.
-func httpHandleRepoTree(writer http.ResponseWriter, request *http.Request, params map[string]any) {
+func (s *server) httpHandleRepoTree(writer http.ResponseWriter, request *http.Request, params map[string]any) {
repoName := params["repo_name"].(string)
groupPath := params["group_path"].([]string)
rawPathSpec := params["rest"].(string)
pathSpec := strings.TrimSuffix(rawPathSpec, "/")
params["path_spec"] = pathSpec
- _, repoPath, _, _, _, _, _ := getRepoInfo(request.Context(), groupPath, repoName, "")
+ _, repoPath, _, _, _, _, _ := s.getRepoInfo(request.Context(), groupPath, repoName, "")
- client, err := git2c.NewClient(config.Git.Socket)
+ client, err := git2c.NewClient(s.config.Git.Socket)
if err != nil {
errorPage500(writer, params, err.Error())
return
diff --git a/http_handle_repo_upload_pack.go b/http_handle_repo_upload_pack.go
index e201193..3d9170c 100644
--- a/http_handle_repo_upload_pack.go
+++ b/http_handle_repo_upload_pack.go
@@ -14,7 +14,7 @@ import (
// httpHandleUploadPack handles incoming Git fetch/pull/clone's over the Smart
// HTTP protocol.
-func httpHandleUploadPack(writer http.ResponseWriter, request *http.Request, params map[string]any) (err error) {
+func (s *server) httpHandleUploadPack(writer http.ResponseWriter, request *http.Request, params map[string]any) (err error) {
var groupPath []string
var repoName string
var repoPath string
@@ -67,7 +67,7 @@ func httpHandleUploadPack(writer http.ResponseWriter, request *http.Request, par
writer.WriteHeader(http.StatusOK)
cmd = exec.Command("git", "upload-pack", "--stateless-rpc", repoPath)
- cmd.Env = append(os.Environ(), "LINDENII_FORGE_HOOKS_SOCKET_PATH="+config.Hooks.Socket)
+ cmd.Env = append(os.Environ(), "LINDENII_FORGE_HOOKS_SOCKET_PATH="+s.config.Hooks.Socket)
if stdout, err = cmd.StdoutPipe(); err != nil {
return err
}
diff --git a/http_server.go b/http_server.go
index 3f8e36c..5c78533 100644
--- a/http_server.go
+++ b/http_server.go
@@ -15,15 +15,13 @@ import (
"go.lindenii.runxiyu.org/forge/misc"
)
-type forgeHTTPRouter struct{}
-
// ServeHTTP handles all incoming HTTP requests and routes them to the correct
// location.
//
// TODO: This function is way too large.
-func (router *forgeHTTPRouter) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
+func (s *server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
var remoteAddr string
- if config.HTTP.ReverseProxy {
+ if s.config.HTTP.ReverseProxy {
remoteAddrs, ok := request.Header["X-Forwarded-For"]
if ok && len(remoteAddrs) == 1 {
remoteAddr = remoteAddrs[0]
@@ -75,7 +73,7 @@ func (router *forgeHTTPRouter) ServeHTTP(writer http.ResponseWriter, request *ht
}
if len(segments) == 0 {
- httpHandleIndex(writer, request, params)
+ s.httpHandleIndex(writer, request, params)
return
}
@@ -100,7 +98,7 @@ func (router *forgeHTTPRouter) ServeHTTP(writer http.ResponseWriter, request *ht
if segments[0] == "-" {
switch segments[1] {
case "login":
- httpHandleLogin(writer, request, params)
+ s.httpHandleLogin(writer, request, params)
return
case "users":
httpHandleUsers(writer, request, params)
@@ -137,7 +135,7 @@ func (router *forgeHTTPRouter) ServeHTTP(writer http.ResponseWriter, request *ht
if misc.RedirectDir(writer, request) {
return
}
- httpHandleGroupIndex(writer, request, params)
+ s.httpHandleGroupIndex(writer, request, params)
case len(segments) == sepIndex+1:
errorPage404(writer, params)
return
@@ -159,7 +157,7 @@ func (router *forgeHTTPRouter) ServeHTTP(writer http.ResponseWriter, request *ht
}
return
case "git-upload-pack":
- if err = httpHandleUploadPack(writer, request, params); err != nil {
+ if err = s.httpHandleUploadPack(writer, request, params); err != nil {
errorPage500(writer, params, err.Error())
}
return
@@ -185,15 +183,15 @@ func (router *forgeHTTPRouter) ServeHTTP(writer http.ResponseWriter, request *ht
repoURLRoot = repoURLRoot + url.PathEscape(part) + "/"
}
params["repo_url_root"] = repoURLRoot
- params["repo_patch_mailing_list"] = repoURLRoot[1:len(repoURLRoot)-1] + "@" + config.LMTP.Domain
- params["http_clone_url"] = genHTTPRemoteURL(groupPath, moduleName)
- params["ssh_clone_url"] = genSSHRemoteURL(groupPath, moduleName)
+ params["repo_patch_mailing_list"] = repoURLRoot[1:len(repoURLRoot)-1] + "@" + s.config.LMTP.Domain
+ params["http_clone_url"] = s.genHTTPRemoteURL(groupPath, moduleName)
+ params["ssh_clone_url"] = s.genSSHRemoteURL(groupPath, moduleName)
if len(segments) == sepIndex+3 {
if misc.RedirectDir(writer, request) {
return
}
- httpHandleRepoIndex(writer, request, params)
+ s.httpHandleRepoIndex(writer, request, params)
return
}
@@ -212,12 +210,12 @@ func (router *forgeHTTPRouter) ServeHTTP(writer http.ResponseWriter, request *ht
if len(segments) < sepIndex+5 && misc.RedirectDir(writer, request) {
return
}
- httpHandleRepoTree(writer, request, params)
+ s.httpHandleRepoTree(writer, request, params)
case "branches":
if misc.RedirectDir(writer, request) {
return
}
- httpHandleRepoBranches(writer, request, params)
+ s.httpHandleRepoBranches(writer, request, params)
return
case "raw":
if misc.AnyContain(segments[sepIndex+4:], "/") {
@@ -232,7 +230,7 @@ func (router *forgeHTTPRouter) ServeHTTP(writer http.ResponseWriter, request *ht
if len(segments) < sepIndex+5 && misc.RedirectDir(writer, request) {
return
}
- httpHandleRepoRaw(writer, request, params)
+ s.httpHandleRepoRaw(writer, request, params)
case "log":
if len(segments) > sepIndex+4 {
errorPage400(writer, params, "Too many parameters")
diff --git a/irc.go b/irc.go
index 868fc05..69cc9f0 100644
--- a/irc.go
+++ b/irc.go
@@ -21,13 +21,13 @@ type errorBack[T any] struct {
errorBack chan error
}
-func ircBotSession() error {
+func (s *server) ircBotSession() error {
var err error
var underlyingConn net.Conn
- if config.IRC.TLS {
- underlyingConn, err = tls.Dial(config.IRC.Net, config.IRC.Addr, nil)
+ if s.config.IRC.TLS {
+ underlyingConn, err = tls.Dial(s.config.IRC.Net, s.config.IRC.Addr, nil)
} else {
- underlyingConn, err = net.Dial(config.IRC.Net, config.IRC.Addr)
+ underlyingConn, err = net.Dial(s.config.IRC.Net, s.config.IRC.Addr)
}
if err != nil {
return err
@@ -41,11 +41,11 @@ func ircBotSession() error {
return conn.WriteString(s + "\r\n")
}
- _, err = logAndWriteLn("NICK " + config.IRC.Nick)
+ _, err = logAndWriteLn("NICK " + s.config.IRC.Nick)
if err != nil {
return err
}
- _, err = logAndWriteLn("USER " + config.IRC.User + " 0 * :" + config.IRC.Gecos)
+ _, err = logAndWriteLn("USER " + s.config.IRC.User + " 0 * :" + s.config.IRC.Gecos)
if err != nil {
return err
}
@@ -86,7 +86,7 @@ func ircBotSession() error {
if !ok {
slog.Error("unable to convert source of JOIN to client")
}
- if c.Nick != config.IRC.Nick {
+ if c.Nick != s.config.IRC.Nick {
continue
}
default:
@@ -134,12 +134,12 @@ func ircSendDirect(s string) error {
}
// TODO: Delay and warnings?
-func ircBotLoop() {
- ircSendBuffered = make(chan string, config.IRC.SendQ)
+func (s *server) ircBotLoop() {
+ ircSendBuffered = make(chan string, s.config.IRC.SendQ)
ircSendDirectChan = make(chan errorBack[string])
for {
- err := ircBotSession()
+ err := s.ircBotSession()
slog.Error("irc session error", "error", err)
}
}
diff --git a/lmtp_server.go b/lmtp_server.go
index fc3d92d..e97ca55 100644
--- a/lmtp_server.go
+++ b/lmtp_server.go
@@ -27,6 +27,7 @@ type lmtpSession struct {
to []string
ctx context.Context
cancel context.CancelFunc
+ s server
}
func (session *lmtpSession) Reset() {
@@ -62,13 +63,13 @@ func (*lmtpHandler) NewSession(_ *smtp.Conn) (smtp.Session, error) {
return session, nil
}
-func serveLMTP(listener net.Listener) error {
+func (s *server) serveLMTP(listener net.Listener) error {
smtpServer := smtp.NewServer(&lmtpHandler{})
smtpServer.LMTP = true
- smtpServer.Domain = config.LMTP.Domain
- smtpServer.Addr = config.LMTP.Socket
- smtpServer.WriteTimeout = time.Duration(config.LMTP.WriteTimeout) * time.Second
- smtpServer.ReadTimeout = time.Duration(config.LMTP.ReadTimeout) * time.Second
+ smtpServer.Domain = s.config.LMTP.Domain
+ smtpServer.Addr = s.config.LMTP.Socket
+ smtpServer.WriteTimeout = time.Duration(s.config.LMTP.WriteTimeout) * time.Second
+ smtpServer.ReadTimeout = time.Duration(s.config.LMTP.ReadTimeout) * time.Second
smtpServer.EnableSMTPUTF8 = true
return smtpServer.Serve(listener)
}
@@ -84,9 +85,9 @@ func (session *lmtpSession) Data(r io.Reader) error {
n int64
)
- n, err = io.CopyN(&buf, r, config.LMTP.MaxSize)
+ n, err = io.CopyN(&buf, r, session.s.config.LMTP.MaxSize)
switch {
- case n == config.LMTP.MaxSize:
+ case n == session.s.config.LMTP.MaxSize:
err = errors.New("Message too big.")
// drain whatever is left in the pipe
_, _ = io.Copy(io.Discard, r)
@@ -107,7 +108,7 @@ func (session *lmtpSession) Data(r io.Reader) error {
switch strings.ToLower(email.Header.Get("Auto-Submitted")) {
case "auto-generated", "auto-replied":
- // Disregard automatic emails like OOO replies.
+ // Disregard automatic emails like OOO repliesession.s.
slog.Info("ignoring automatic message",
"from", session.from,
"to", strings.Join(session.to, ","),
@@ -132,10 +133,10 @@ func (session *lmtpSession) Data(r io.Reader) error {
_ = from
for _, to := range to {
- if !strings.HasSuffix(to, "@"+config.LMTP.Domain) {
+ if !strings.HasSuffix(to, "@"+session.s.config.LMTP.Domain) {
continue
}
- localPart := to[:len(to)-len("@"+config.LMTP.Domain)]
+ localPart := to[:len(to)-len("@"+session.s.config.LMTP.Domain)]
var segments []string
segments, err = misc.PathToSegments(localPart)
if err != nil {
diff --git a/main.go b/main.go
index de01417..4527825 100644
--- a/main.go
+++ b/main.go
@@ -24,11 +24,13 @@ func main() {
)
flag.Parse()
- if err := loadConfig(*configPath); err != nil {
+ s := server{}
+
+ if err := s.loadConfig(*configPath); err != nil {
slog.Error("loading configuration", "error", err)
os.Exit(1)
}
- if err := deployHooks(); err != nil {
+ if err := s.deployHooks(); err != nil {
slog.Error("deploying hooks", "error", err)
os.Exit(1)
}
@@ -36,14 +38,14 @@ func main() {
slog.Error("loading templates", "error", err)
os.Exit(1)
}
- if err := deployGit2D(); err != nil {
+ if err := s.deployGit2D(); err != nil {
slog.Error("deploying git2d", "error", err)
os.Exit(1)
}
// Launch Git2D
go func() {
- cmd := exec.Command(config.Git.DaemonPath, config.Git.Socket) //#nosec G204
+ cmd := exec.Command(s.config.Git.DaemonPath, s.config.Git.Socket) //#nosec G204
cmd.Stderr = log.Writer()
cmd.Stdout = log.Writer()
if err := cmd.Run(); err != nil {
@@ -53,14 +55,14 @@ func main() {
// UNIX socket listener for hooks
{
- hooksListener, err := net.Listen("unix", config.Hooks.Socket)
+ hooksListener, err := net.Listen("unix", s.config.Hooks.Socket)
if errors.Is(err, syscall.EADDRINUSE) {
- slog.Warn("removing existing socket", "path", config.Hooks.Socket)
- if err = syscall.Unlink(config.Hooks.Socket); err != nil {
- slog.Error("removing existing socket", "path", config.Hooks.Socket, "error", err)
+ slog.Warn("removing existing socket", "path", s.config.Hooks.Socket)
+ if err = syscall.Unlink(s.config.Hooks.Socket); err != nil {
+ slog.Error("removing existing socket", "path", s.config.Hooks.Socket, "error", err)
os.Exit(1)
}
- if hooksListener, err = net.Listen("unix", config.Hooks.Socket); err != nil {
+ if hooksListener, err = net.Listen("unix", s.config.Hooks.Socket); err != nil {
slog.Error("listening hooks", "error", err)
os.Exit(1)
}
@@ -68,9 +70,9 @@ func main() {
slog.Error("listening hooks", "error", err)
os.Exit(1)
}
- slog.Info("listening hooks on unix", "path", config.Hooks.Socket)
+ slog.Info("listening hooks on unix", "path", s.config.Hooks.Socket)
go func() {
- if err = serveGitHooks(hooksListener); err != nil {
+ if err = s.serveGitHooks(hooksListener); err != nil {
slog.Error("serving hooks", "error", err)
os.Exit(1)
}
@@ -79,14 +81,14 @@ func main() {
// UNIX socket listener for LMTP
{
- lmtpListener, err := net.Listen("unix", config.LMTP.Socket)
+ lmtpListener, err := net.Listen("unix", s.config.LMTP.Socket)
if errors.Is(err, syscall.EADDRINUSE) {
- slog.Warn("removing existing socket", "path", config.LMTP.Socket)
- if err = syscall.Unlink(config.LMTP.Socket); err != nil {
- slog.Error("removing existing socket", "path", config.LMTP.Socket, "error", err)
+ slog.Warn("removing existing socket", "path", s.config.LMTP.Socket)
+ if err = syscall.Unlink(s.config.LMTP.Socket); err != nil {
+ slog.Error("removing existing socket", "path", s.config.LMTP.Socket, "error", err)
os.Exit(1)
}
- if lmtpListener, err = net.Listen("unix", config.LMTP.Socket); err != nil {
+ if lmtpListener, err = net.Listen("unix", s.config.LMTP.Socket); err != nil {
slog.Error("listening LMTP", "error", err)
os.Exit(1)
}
@@ -94,9 +96,9 @@ func main() {
slog.Error("listening LMTP", "error", err)
os.Exit(1)
}
- slog.Info("listening LMTP on unix", "path", config.LMTP.Socket)
+ slog.Info("listening LMTP on unix", "path", s.config.LMTP.Socket)
go func() {
- if err = serveLMTP(lmtpListener); err != nil {
+ if err = s.serveLMTP(lmtpListener); err != nil {
slog.Error("serving LMTP", "error", err)
os.Exit(1)
}
@@ -105,14 +107,14 @@ func main() {
// SSH listener
{
- sshListener, err := net.Listen(config.SSH.Net, config.SSH.Addr)
- if errors.Is(err, syscall.EADDRINUSE) && config.SSH.Net == "unix" {
- slog.Warn("removing existing socket", "path", config.SSH.Addr)
- if err = syscall.Unlink(config.SSH.Addr); err != nil {
- slog.Error("removing existing socket", "path", config.SSH.Addr, "error", err)
+ sshListener, err := net.Listen(s.config.SSH.Net, s.config.SSH.Addr)
+ if errors.Is(err, syscall.EADDRINUSE) && s.config.SSH.Net == "unix" {
+ slog.Warn("removing existing socket", "path", s.config.SSH.Addr)
+ if err = syscall.Unlink(s.config.SSH.Addr); err != nil {
+ slog.Error("removing existing socket", "path", s.config.SSH.Addr, "error", err)
os.Exit(1)
}
- if sshListener, err = net.Listen(config.SSH.Net, config.SSH.Addr); err != nil {
+ if sshListener, err = net.Listen(s.config.SSH.Net, s.config.SSH.Addr); err != nil {
slog.Error("listening SSH", "error", err)
os.Exit(1)
}
@@ -120,9 +122,9 @@ func main() {
slog.Error("listening SSH", "error", err)
os.Exit(1)
}
- slog.Info("listening SSH on", "net", config.SSH.Net, "addr", config.SSH.Addr)
+ slog.Info("listening SSH on", "net", s.config.SSH.Net, "addr", s.config.SSH.Addr)
go func() {
- if err = serveSSH(sshListener); err != nil {
+ if err = s.serveSSH(sshListener); err != nil {
slog.Error("serving SSH", "error", err)
os.Exit(1)
}
@@ -131,14 +133,14 @@ func main() {
// HTTP listener
{
- httpListener, err := net.Listen(config.HTTP.Net, config.HTTP.Addr)
- if errors.Is(err, syscall.EADDRINUSE) && config.HTTP.Net == "unix" {
- slog.Warn("removing existing socket", "path", config.HTTP.Addr)
- if err = syscall.Unlink(config.HTTP.Addr); err != nil {
- slog.Error("removing existing socket", "path", config.HTTP.Addr, "error", err)
+ httpListener, err := net.Listen(s.config.HTTP.Net, s.config.HTTP.Addr)
+ if errors.Is(err, syscall.EADDRINUSE) && s.config.HTTP.Net == "unix" {
+ slog.Warn("removing existing socket", "path", s.config.HTTP.Addr)
+ if err = syscall.Unlink(s.config.HTTP.Addr); err != nil {
+ slog.Error("removing existing socket", "path", s.config.HTTP.Addr, "error", err)
os.Exit(1)
}
- if httpListener, err = net.Listen(config.HTTP.Net, config.HTTP.Addr); err != nil {
+ if httpListener, err = net.Listen(s.config.HTTP.Net, s.config.HTTP.Addr); err != nil {
slog.Error("listening HTTP", "error", err)
os.Exit(1)
}
@@ -147,12 +149,12 @@ func main() {
os.Exit(1)
}
server := http.Server{
- Handler: &forgeHTTPRouter{},
- ReadTimeout: time.Duration(config.HTTP.ReadTimeout) * time.Second,
- WriteTimeout: time.Duration(config.HTTP.ReadTimeout) * time.Second,
- IdleTimeout: time.Duration(config.HTTP.ReadTimeout) * time.Second,
+ Handler: &s,
+ ReadTimeout: time.Duration(s.config.HTTP.ReadTimeout) * time.Second,
+ WriteTimeout: time.Duration(s.config.HTTP.ReadTimeout) * time.Second,
+ IdleTimeout: time.Duration(s.config.HTTP.ReadTimeout) * time.Second,
} //exhaustruct:ignore
- slog.Info("listening HTTP on", "net", config.HTTP.Net, "addr", config.HTTP.Addr)
+ slog.Info("listening HTTP on", "net", s.config.HTTP.Net, "addr", s.config.HTTP.Addr)
go func() {
if err = server.Serve(httpListener); err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Error("serving HTTP", "error", err)
@@ -162,7 +164,7 @@ func main() {
}
// IRC bot
- go ircBotLoop()
+ go s.ircBotLoop()
select {}
}
diff --git a/remote_url.go b/remote_url.go
index f227dbf..9f30993 100644
--- a/remote_url.go
+++ b/remote_url.go
@@ -14,12 +14,12 @@ import (
// genSSHRemoteURL generates SSH remote URLs from a given group path and repo
// name.
-func genSSHRemoteURL(groupPath []string, repoName string) string {
- return strings.TrimSuffix(config.SSH.Root, "/") + "/" + misc.SegmentsToURL(groupPath) + "/-/repos/" + url.PathEscape(repoName)
+func (s *server) genSSHRemoteURL(groupPath []string, repoName string) string {
+ return strings.TrimSuffix(s.config.SSH.Root, "/") + "/" + misc.SegmentsToURL(groupPath) + "/-/repos/" + url.PathEscape(repoName)
}
// genHTTPRemoteURL generates HTTP remote URLs from a given group path and repo
// name.
-func genHTTPRemoteURL(groupPath []string, repoName string) string {
- return strings.TrimSuffix(config.HTTP.Root, "/") + "/" + misc.SegmentsToURL(groupPath) + "/-/repos/" + url.PathEscape(repoName)
+func (s *server) genHTTPRemoteURL(groupPath []string, repoName string) string {
+ return strings.TrimSuffix(s.config.HTTP.Root, "/") + "/" + misc.SegmentsToURL(groupPath) + "/-/repos/" + url.PathEscape(repoName)
}
diff --git a/server.go b/server.go
new file mode 100644
index 0000000..8f35913
--- /dev/null
+++ b/server.go
@@ -0,0 +1,5 @@
+package main
+
+type server struct {
+ config Config
+}
diff --git a/ssh_handle_receive_pack.go b/ssh_handle_receive_pack.go
index f8777fa..ed7ef40 100644
--- a/ssh_handle_receive_pack.go
+++ b/ssh_handle_receive_pack.go
@@ -34,8 +34,8 @@ type packPass struct {
var packPasses = cmap.Map[string, packPass]{}
// sshHandleRecvPack handles attempts to push to repos.
-func sshHandleRecvPack(session gliderSSH.Session, pubkey, repoIdentifier string) (err error) {
- groupPath, repoName, repoID, repoPath, directAccess, contribReq, userType, userID, err := getRepoInfo2(session.Context(), repoIdentifier, pubkey)
+func (s *server) sshHandleRecvPack(session gliderSSH.Session, pubkey, repoIdentifier string) (err error) {
+ groupPath, repoName, repoID, repoPath, directAccess, contribReq, userType, userID, err := s.getRepoInfo2(session.Context(), repoIdentifier, pubkey)
if err != nil {
return err
}
@@ -55,7 +55,7 @@ func sshHandleRecvPack(session gliderSSH.Session, pubkey, repoIdentifier string)
}
hooksPath := repoConfCore.OptionAll("hooksPath")
- if len(hooksPath) != 1 || hooksPath[0] != config.Hooks.Execs {
+ if len(hooksPath) != 1 || hooksPath[0] != s.config.Hooks.Execs {
return errors.New("repository has hooksPath set to an unexpected value")
}
@@ -114,7 +114,7 @@ func sshHandleRecvPack(session gliderSSH.Session, pubkey, repoIdentifier string)
proc := exec.CommandContext(session.Context(), "git-receive-pack", repoPath)
proc.Env = append(os.Environ(),
- "LINDENII_FORGE_HOOKS_SOCKET_PATH="+config.Hooks.Socket,
+ "LINDENII_FORGE_HOOKS_SOCKET_PATH="+s.config.Hooks.Socket,
"LINDENII_FORGE_HOOKS_COOKIE="+cookie,
)
proc.Stdin = session
diff --git a/ssh_handle_upload_pack.go b/ssh_handle_upload_pack.go
index d732a72..7f2a52c 100644
--- a/ssh_handle_upload_pack.go
+++ b/ssh_handle_upload_pack.go
@@ -13,14 +13,14 @@ import (
// sshHandleUploadPack handles clones/fetches. It just uses git-upload-pack
// and has no ACL checks.
-func sshHandleUploadPack(session glider_ssh.Session, pubkey, repoIdentifier string) (err error) {
+func (s *server) sshHandleUploadPack(session glider_ssh.Session, pubkey, repoIdentifier string) (err error) {
var repoPath string
- if _, _, _, repoPath, _, _, _, _, err = getRepoInfo2(session.Context(), repoIdentifier, pubkey); err != nil {
+ if _, _, _, repoPath, _, _, _, _, err = s.getRepoInfo2(session.Context(), repoIdentifier, pubkey); err != nil {
return err
}
proc := exec.CommandContext(session.Context(), "git-upload-pack", repoPath)
- proc.Env = append(os.Environ(), "LINDENII_FORGE_HOOKS_SOCKET_PATH="+config.Hooks.Socket)
+ proc.Env = append(os.Environ(), "LINDENII_FORGE_HOOKS_SOCKET_PATH="+s.config.Hooks.Socket)
proc.Stdin = session
proc.Stdout = session
proc.Stderr = session.Stderr()
diff --git a/ssh_server.go b/ssh_server.go
index c1b8c8a..9cb3062 100644
--- a/ssh_server.go
+++ b/ssh_server.go
@@ -25,13 +25,13 @@ var (
// serveSSH serves SSH on a [net.Listener]. The listener should generally be a
// TCP listener, although AF_UNIX SOCK_STREAM listeners may be appropriate in
// rare cases.
-func serveSSH(listener net.Listener) error {
+func (s *server) serveSSH(listener net.Listener) error {
var hostKeyBytes []byte
var hostKey goSSH.Signer
var err error
var server *gliderSSH.Server
- if hostKeyBytes, err = os.ReadFile(config.SSH.Key); err != nil {
+ if hostKeyBytes, err = os.ReadFile(s.config.SSH.Key); err != nil {
return err
}
@@ -52,7 +52,7 @@ func serveSSH(listener net.Listener) error {
}
slog.Info("incoming ssh", "addr", session.RemoteAddr().String(), "key", clientPubkeyStr, "command", session.RawCommand())
- fmt.Fprintln(session.Stderr(), ansiec.Blue+"Lindenii Forge "+VERSION+", source at "+strings.TrimSuffix(config.HTTP.Root, "/")+"/-/source/"+ansiec.Reset+"\r")
+ fmt.Fprintln(session.Stderr(), ansiec.Blue+"Lindenii Forge "+VERSION+", source at "+strings.TrimSuffix(s.config.HTTP.Root, "/")+"/-/source/"+ansiec.Reset+"\r")
cmd := session.Command()
@@ -67,13 +67,13 @@ func serveSSH(listener net.Listener) error {
fmt.Fprintln(session.Stderr(), "Too many arguments\r")
return
}
- err = sshHandleUploadPack(session, clientPubkeyStr, cmd[1])
+ err = s.sshHandleUploadPack(session, clientPubkeyStr, cmd[1])
case "git-receive-pack":
if len(cmd) > 2 {
fmt.Fprintln(session.Stderr(), "Too many arguments\r")
return
}
- err = sshHandleRecvPack(session, clientPubkeyStr, cmd[1])
+ err = s.sshHandleRecvPack(session, clientPubkeyStr, cmd[1])
default:
fmt.Fprintln(session.Stderr(), "Unsupported command: "+cmd[0]+"\r")
return
diff --git a/ssh_utils.go b/ssh_utils.go
index c906ab3..02069dd 100644
--- a/ssh_utils.go
+++ b/ssh_utils.go
@@ -18,7 +18,7 @@ var errIllegalSSHRepoPath = errors.New("illegal SSH repo path")
// getRepoInfo2 also fetches repo information... it should be deprecated and
// implemented in individual handlers.
-func getRepoInfo2(ctx context.Context, sshPath, sshPubkey string) (groupPath []string, repoName string, repoID int, repoPath string, directAccess bool, contribReq, userType string, userID int, err error) {
+func (s *server) getRepoInfo2(ctx context.Context, sshPath, sshPubkey string) (groupPath []string, repoName string, repoID int, repoPath string, directAccess bool, contribReq, userType string, userID int, err error) {
var segments []string
var sepIndex int
var moduleType, moduleName string
@@ -64,7 +64,7 @@ func getRepoInfo2(ctx context.Context, sshPath, sshPubkey string) (groupPath []s
repoName = moduleName
switch moduleType {
case "repos":
- _1, _2, _3, _4, _5, _6, _7 := getRepoInfo(ctx, groupPath, moduleName, sshPubkey)
+ _1, _2, _3, _4, _5, _6, _7 := s.getRepoInfo(ctx, groupPath, moduleName, sshPubkey)
return groupPath, repoName, _1, _2, _3, _4, _5, _6, _7
default:
return []string{}, "", 0, "", false, "", "", 0, errIllegalSSHRepoPath