vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
refactor
c5902451905acdaa478f8476844ccd4a510d9ca9 · Keenan Nemetz · 16d ago · parent ec2aba4b
internal/server/server.go → server/server.go +35−68
| @@ -8,22 +8,20 @@ import ( | |||
| 8 | 8 | "log/slog" | |
| 9 | 9 | "net" | |
| 10 | 10 | "net/http" | |
| 11 | "os" | ||
| 12 | 11 | "strconv" | |
| 13 | 12 | "time" | |
| 14 | 13 | ||
| 15 | "github.com/rodent-software/vole/internal/access" | ||
| 16 | "github.com/rodent-software/vole/internal/config" | ||
| 17 | "github.com/rodent-software/vole/internal/identity" | ||
| 18 | "github.com/rodent-software/vole/internal/index" | ||
| 19 | "github.com/rodent-software/vole/internal/lfs" | ||
| 20 | "github.com/rodent-software/vole/internal/locks" | ||
| 21 | "github.com/rodent-software/vole/internal/pages" | ||
| 22 | "github.com/rodent-software/vole/internal/repo" | ||
| 23 | "github.com/rodent-software/vole/internal/runs" | ||
| 24 | "github.com/rodent-software/vole/internal/sshserver" | ||
| 25 | "github.com/rodent-software/vole/internal/transport" | ||
| 26 | "github.com/rodent-software/vole/internal/web" | ||
| 14 | "github.com/rodent-software/vole/ci/runs" | ||
| 15 | "github.com/rodent-software/vole/core/lfs" | ||
| 16 | "github.com/rodent-software/vole/forge" | ||
| 17 | "github.com/rodent-software/vole/forge/config" | ||
| 18 | "github.com/rodent-software/vole/forge/identity" | ||
| 19 | "github.com/rodent-software/vole/forge/index" | ||
| 20 | "github.com/rodent-software/vole/forge/repo" | ||
| 21 | "github.com/rodent-software/vole/server/githttp" | ||
| 22 | "github.com/rodent-software/vole/server/pages" | ||
| 23 | "github.com/rodent-software/vole/server/ssh" | ||
| 24 | "github.com/rodent-software/vole/server/web" | ||
| 27 | 25 | ) | |
| 28 | 26 | ||
| 29 | 27 | type Server struct { | |
| @@ -35,10 +33,10 @@ type Server struct { | |||
| 35 | 33 | Runs *runs.Store | |
| 36 | 34 | Index *index.Store | |
| 37 | 35 | LFS *lfs.Store | |
| 38 | Locks *locks.Store | ||
| 36 | Locks *lfs.LockStore | ||
| 39 | 37 | Pages *pages.Server | |
| 40 | Proxy *transport.Proxy | ||
| 41 | SSH *sshserver.Server | ||
| 38 | Proxy *githttp.Proxy | ||
| 39 | SSH *ssh.Server | ||
| 42 | 40 | Web *web.Web | |
| 43 | 41 | Log *slog.Logger | |
| 44 | 42 | mux *http.ServeMux | |
| @@ -48,60 +46,27 @@ func New(cfg *config.Config, binPath string, log *slog.Logger) (*Server, error) | |||
| 48 | 46 | if log == nil { | |
| 49 | 47 | log = slog.Default() | |
| 50 | 48 | } | |
| 51 | if err := os.MkdirAll(cfg.ReposDir(), 0o755); err != nil { | ||
| 52 | return nil, err | ||
| 53 | } | ||
| 54 | ident, err := identity.Open(cfg.UsersDB()) | ||
| 55 | if err != nil { | ||
| 56 | return nil, fmt.Errorf("%w\nrun `vole init` to create the instance", err) | ||
| 57 | } | ||
| 58 | ident.AuditPath = cfg.AuditLog() | ||
| 59 | ix, err := index.Open(cfg.IndexDB()) | ||
| 60 | if err != nil { | ||
| 61 | return nil, fmt.Errorf("open index: %w", err) | ||
| 62 | } | ||
| 63 | runStore, err := runs.Open(cfg.RunsDB()) | ||
| 49 | inst, err := forge.Open(cfg, binPath) | ||
| 64 | 50 | if err != nil { | |
| 65 | return nil, fmt.Errorf("open run queue: %w", err) | ||
| 66 | } | ||
| 67 | // Access rules live in the instance database beside the roster: only the instance | ||
| 68 | // writes them, and they are keyed by account id, so a copy inside a repository | ||
| 69 | // would be unreadable anyway. | ||
| 70 | acls, err := access.Open(cfg.UsersDB()) | ||
| 71 | if err != nil { | ||
| 72 | return nil, fmt.Errorf("open access rules: %w", err) | ||
| 73 | } | ||
| 74 | acls.AuditPath = cfg.AuditLog() | ||
| 75 | // Locks live in the same database as the roster and the access rules: all three are | ||
| 76 | // keyed by account id and none of them can be rebuilt from git, so they are backed | ||
| 77 | // up and restored together or the ids stop resolving. | ||
| 78 | lockStore, err := locks.Open(cfg.UsersDB()) | ||
| 79 | if err != nil { | ||
| 80 | return nil, fmt.Errorf("open locks: %w", err) | ||
| 51 | return nil, err | ||
| 81 | 52 | } | |
| 82 | lockStore.AuditPath = cfg.AuditLog() | ||
| 83 | rs := repo.New(cfg.ReposDir(), binPath, acls) | ||
| 84 | rs.MaxInputBytes = cfg.ReceiveMaxBytes | ||
| 85 | if n, err := rs.ImportACLs(); err != nil { | ||
| 86 | log.Warn("import access rules from refs/vole/acl", "err", err) | ||
| 87 | } else if n > 0 { | ||
| 88 | log.Info("access rules imported", "repos", n) | ||
| 53 | if inst.ImportedACLs > 0 { | ||
| 54 | log.Info("access rules imported", "repos", inst.ImportedACLs) | ||
| 89 | 55 | } | |
| 90 | if n, err := rs.InstallAllHooks(); err != nil { | ||
| 56 | if n, err := inst.Repos.InstallAllHooks(); err != nil { | ||
| 91 | 57 | log.Warn("install hooks", "err", err) | |
| 92 | 58 | } else if n > 0 { | |
| 93 | 59 | log.Info("hooks installed", "repos", n) | |
| 94 | 60 | } | |
| 95 | proxy := transport.New(transport.Config{ | ||
| 61 | proxy := githttp.New(githttp.Config{ | ||
| 96 | 62 | Root: cfg.ReposDir(), | |
| 97 | Open: rs.Open, | ||
| 63 | Open: inst.Repos.Open, | ||
| 98 | 64 | Log: log, | |
| 99 | 65 | }) | |
| 100 | lfsStore := lfs.New(cfg.LFSDir(), cfg.LFSObjectMaxBytes) | ||
| 101 | ssh, err := sshserver.New(sshserver.Config{ | ||
| 102 | Identity: ident, | ||
| 103 | Repos: rs, | ||
| 104 | LFS: lfsStore, | ||
| 66 | ssh, err := ssh.New(ssh.Config{ | ||
| 67 | Identity: inst.Identity, | ||
| 68 | Repos: inst.Repos, | ||
| 69 | LFS: inst.LFS, | ||
| 105 | 70 | LFSWarnBytes: cfg.LFSWarnBytes, | |
| 106 | 71 | Log: log, | |
| 107 | 72 | HostKeyPath: cfg.SSHHostKey(), | |
| @@ -109,27 +74,29 @@ func New(cfg *config.Config, binPath string, log *slog.Logger) (*Server, error) | |||
| 109 | 74 | AuditPath: cfg.AuditLog(), | |
| 110 | 75 | SpoolDir: cfg.SpoolDir(), | |
| 111 | 76 | RunsDB: cfg.RunsDB(), | |
| 112 | Runs: runStore, | ||
| 113 | Locks: lockStore, | ||
| 77 | Runs: inst.Runs, | ||
| 78 | Locks: inst.Locks, | ||
| 114 | 79 | Lease: cfg.Lease(), | |
| 115 | 80 | MaxJobsPerRepo: cfg.MaxJobsPerRepo, | |
| 116 | 81 | PagesRef: cfg.PagesRef, | |
| 117 | 82 | PublicURL: cfg.BaseURL(), | |
| 118 | 83 | }) | |
| 119 | 84 | if err != nil { | |
| 85 | inst.Close() | ||
| 120 | 86 | return nil, fmt.Errorf("ssh: %w", err) | |
| 121 | 87 | } | |
| 122 | wp, err := web.New(rs, ix, ident, runStore, cfg) | ||
| 88 | wp, err := web.New(inst.Repos, inst.Index, inst.Identity, inst.Runs, cfg) | ||
| 123 | 89 | if err != nil { | |
| 90 | inst.Close() | ||
| 124 | 91 | return nil, err | |
| 125 | 92 | } | |
| 126 | wp.LFS = lfsStore | ||
| 127 | wp.Locks = lockStore | ||
| 93 | wp.LFS = inst.LFS | ||
| 94 | wp.Locks = inst.Locks | ||
| 128 | 95 | wp.Logger = func(format string, args ...any) { log.Warn(fmt.Sprintf(format, args...)) } | |
| 129 | 96 | ||
| 130 | 97 | s := &Server{ | |
| 131 | Cfg: cfg, Repos: rs, Identity: ident, Runs: runStore, Index: ix, LFS: lfsStore, Locks: lockStore, | ||
| 132 | Proxy: proxy, SSH: ssh, Web: wp, Log: log, | ||
| 98 | Cfg: cfg, Repos: inst.Repos, Identity: inst.Identity, Runs: inst.Runs, Index: inst.Index, | ||
| 99 | LFS: inst.LFS, Locks: inst.Locks, Proxy: proxy, SSH: ssh, Web: wp, Log: log, | ||
| 133 | 100 | } | |
| 134 | 101 | // Sites are opt-in: without a pages domain there is no host to serve them on, | |
| 135 | 102 | // and an instance that never wanted them pays no processes for the idea. | |
| @@ -140,7 +107,7 @@ func New(cfg *config.Config, binPath string, log *slog.Logger) (*Server, error) | |||
| 140 | 107 | Procs: cfg.PagesProcs, | |
| 141 | 108 | CacheBytes: cfg.PagesCacheBytes, | |
| 142 | 109 | Log: log, | |
| 143 | }, rs) | ||
| 110 | }, inst.Repos) | ||
| 144 | 111 | } | |
| 145 | 112 | s.mux = s.routes() | |
| 146 | 113 | // Background loops start here rather than in Run: a caller that serves the | |