package common import ( "errors" "fmt" "os" "path/filepath" "strings" "sync/atomic" ) type Config struct { Root string // SSHAddr: git SSH listen (clone/push over SSH, keyed auth). SSHAddr string // HTTPAddr: plain HTTP listen for git smart-HTTP clone/push // (/:owner/:name.git/*) + the index page. This is the user-facing git // transport (nginx routes .git/* here). Empty = no HTTP listener. HTTPAddr string // GitRPCAddrs: Connect-RPC listen addresses (GitService + ManageService) // PLUS the binary /raw, /archive, /patch content endpoints. Others dial // these to read/write/modify repo content and drive repo lifecycle. May be // multiple (e.g. a unix socket for a local TUI and a tcp addr for the // console). Empty = no gitrpc listener. GitRPCAddrs []string // HookScript is an operator-provided executable invoked by every Git hook. // The hook name is its first argument; Git's original args, stdin and // environment are preserved. Empty disables hook effects. HookScript string DBUri string preparedDbUri string prepared atomic.Bool driver string dsn string } func (c *Config) RepoRoot() string { return filepath.Join(c.Root, "repos") } func (c *Config) TemplateDir() string { return filepath.Join(c.Root, "template") } func (c *Config) Prepare() error { if c.prepared.Load() { return nil } var err error if c.DBUri == "" { return errors.New("simplegit: -db is required (e.g. sqlite://state.db)") } if err := os.MkdirAll(c.RepoRoot(), 0o755); err != nil { return fmt.Errorf("simplegit: Failed to create data root: %s (%v)", c.RepoRoot(), err) } c.driver, c.dsn, c.preparedDbUri, err = ParseDBURI(c.Root, c.DBUri) if err != nil { return errors.New("simplegit: ParseDBURI failed") } // Listeners are all optional: a daemon with only -ssh, only -http, only // -gitrpc, or any combination is valid (empty = that surface is disabled). // At least one of -http/-ssh/-gitrpc should be set for the daemon to do // anything useful, but that is the operator's choice, not a hard error. c.prepared.Store(true) return nil } func (c *Config) Driver() string { if !c.prepared.Load() { panic("Config.Driver called before Prepare") } return c.driver } func (c *Config) DSN() string { if !c.prepared.Load() { panic("Config.DSN called before Prepare") } return c.dsn } func (c *Config) DBURI() string { if !c.prepared.Load() { panic("Config.PreparedDBUri called before Prepare") } return c.preparedDbUri } func normalizeName(name string) string { return strings.TrimSuffix(name, ".git") } func relPath(owner, name string) string { return owner + "/" + normalizeName(name) + ".git" } func (s *Config) RepoPath(owner, name string) (string, error) { name = normalizeName(name) rel := relPath(owner, name) return Resolve(s.RepoRoot(), rel) }