repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

Eric Bower  ·  2026-02-24

cfg.go

  1package git
  2
  3import (
  4	"fmt"
  5	"log/slog"
  6	"path/filepath"
  7	"strings"
  8	"time"
  9
 10	"github.com/knadh/koanf/parsers/toml"
 11	"github.com/knadh/koanf/providers/env"
 12	"github.com/knadh/koanf/providers/file"
 13	"github.com/knadh/koanf/v2"
 14	"golang.org/x/crypto/ssh"
 15)
 16
 17var k = koanf.New(".")
 18
 19type GitCfg struct {
 20	DataDir    string          `koanf:"data_dir"`
 21	Url        string          `koanf:"url"`
 22	Host       string          `koanf:"host"`
 23	SshPort    string          `koanf:"ssh_port"`
 24	WebPort    string          `koanf:"web_port"`
 25	PromPort   string          `koanf:"prom_port"`
 26	AdminsStr  []string        `koanf:"admins"`
 27	Admins     []ssh.PublicKey `koanf:"admins_pk"`
 28	CreateRepo string          `koanf:"create_repo"`
 29	Theme      string          `koanf:"theme"`
 30	TimeFormat string          `koanf:"time_format"`
 31	Desc       string          `koanf:"desc"`
 32	Logger     *slog.Logger
 33}
 34
 35func LoadConfigFile(fpath string, logger *slog.Logger) {
 36	fpp, err := filepath.Abs(fpath)
 37	if err != nil {
 38		panic(err)
 39	}
 40	logger.Info("loading configuration file", "fpath", fpp)
 41
 42	if err := k.Load(file.Provider(fpp), toml.Parser()); err != nil {
 43		panic(fmt.Sprintf("error loading config: %v", err))
 44	}
 45}
 46
 47func NewGitCfg(logger *slog.Logger) *GitCfg {
 48	err := k.Load(env.Provider("GITPR_", ".", func(s string) string {
 49		keyword := strings.ToLower(strings.TrimPrefix(s, "GITPR_"))
 50		return keyword
 51	}), nil)
 52	if err != nil {
 53		panic(fmt.Sprintf("could not load environment variables: %v", err))
 54	}
 55
 56	var out GitCfg
 57	err = k.UnmarshalWithConf("", &out, koanf.UnmarshalConf{Tag: "koanf"})
 58	if err != nil {
 59		panic(fmt.Sprintf("could not unmarshal config: %v", err))
 60	}
 61
 62	if len(out.AdminsStr) > 0 {
 63		keys, err := GetAuthorizedKeys(out.AdminsStr)
 64		if err == nil {
 65			out.Admins = keys
 66		} else {
 67			panic(fmt.Sprintf("could not parse authorized keys file: %v", err))
 68		}
 69	} else {
 70		logger.Info("no admin specified in config so no one can submit a review!")
 71	}
 72
 73	// make datadir absolute
 74	tmpdir := out.DataDir
 75	if out.DataDir == "" {
 76		tmpdir = "./data"
 77	}
 78	datadir, err := filepath.Abs(tmpdir)
 79	out.DataDir = datadir
 80	if err != nil {
 81		panic(err)
 82	}
 83
 84	if out.Host == "" {
 85		out.Host = "0.0.0.0"
 86	}
 87
 88	if out.SshPort == "" {
 89		out.SshPort = "2222"
 90	}
 91
 92	if out.WebPort == "" {
 93		out.WebPort = "3000"
 94	}
 95
 96	if out.Theme == "" {
 97		out.Theme = "dracula"
 98	}
 99
100	if out.TimeFormat == "" {
101		out.TimeFormat = time.RFC3339
102	}
103
104	if out.CreateRepo == "" {
105		out.CreateRepo = "admin"
106	}
107
108	logger.Info(
109		"config",
110		"url", out.Url,
111		"data_dir", out.DataDir,
112		"host", out.Host,
113		"ssh_port", out.SshPort,
114		"web_port", out.WebPort,
115		"theme", out.Theme,
116		"time_format", out.TimeFormat,
117		"create_repo", out.CreateRepo,
118		"desc", out.Desc,
119	)
120
121	for _, pubkey := range out.AdminsStr {
122		logger.Info("admin", "pubkey", pubkey)
123	}
124
125	out.Logger = logger
126	return &out
127}