package common import ( "errors" "fmt" "os" "path/filepath" "strings" "sync/atomic" ) // Config carries the daemon's startup params. The server needs the git root to // open repos and the rest to answer the Status RPC (which also hands gitctl the // db_uri so it can connect directly for inspection reads). type Config struct { Root string RPCAddr string SSHAddr string ManageAddr string AuthURL string DBUri string SkipAuth bool preparedDbUri string prepared atomic.Bool driver string dsn string } func (c *Config) RepoRoot() string { return filepath.Join(c.Root, "repos") } // Prepare checks that the config is valid and creates the repo root if needed. 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") } if c.RPCAddr == "" { return errors.New("simplegit: -rpc is required (the HTTP server is this binary's only listener)") } if c.ManageAddr == "" { return fmt.Errorf("*manageAddr == nil") } 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 } // normalizeName strips one trailing ".git" so both "myrepo" and "myrepo.git" // refer to the same repo. func normalizeName(name string) string { return strings.TrimSuffix(name, ".git") } // relPath returns the repo path relative to root: "owner/name.git". func relPath(owner, name string) string { return owner + "/" + normalizeName(name) + ".git" } // RepoPath returns the absolute on-disk path for owner/name, but only if the // repo is registered in the DB -- the DB is the authority, not the filesystem. // In skip-auth mode the DB check is skipped (any path under root resolves). func (s *Config) RepoPath(owner, name string) (string, error) { name = normalizeName(name) rel := relPath(owner, name) return Resolve(s.RepoRoot(), rel) }