// Command gsg serves a modular git browser. // // gsg -root /srv/git -addr :8080 -name "rschanz.org" // gsg -root /srv/git -templates ./templates # hot-reload the theme // // Per-repository settings live in each repo's own git config, cgit-style: // // git --git-dir=/srv/git/foo.git config gsg.title "foo" // git --git-dir=/srv/git/foo.git config gsg.description "a thing" // git --git-dir=/srv/git/foo.git config gsg.hide true // git --git-dir=/srv/git/foo.git config gsg.gpgkeyring /srv/git/foo-keys.asc package main import ( "embed" "flag" "io/fs" "log" "log/slog" "net/http" "os" "time" "gsg/internal/core" _ "gsg/internal/modules" // registers the built-in modules "gsg/internal/site" ) //go:embed templates var templateFS embed.FS //go:embed static var staticFS embed.FS func main() { var ( root = flag.String("root", "/srv/git", "directory containing bare repositories") addr = flag.String("addr", ":8080", "listen address") name = flag.String("name", "git", "site name") base = flag.String("base", "", "base URL") tmplDir = flag.String("templates", "", "serve templates from this directory, re-parsed per request") cache = flag.Int("cache", 4096, "module cache entries") timeout = flag.Duration("timeout", 10*time.Second, "per-request git budget") ) flag.Parse() if err := run(*root, *addr, *name, *base, *tmplDir, *cache, *timeout); err != nil { log.Fatalf("gsg: %v", err) } } func run(root, addr, name, base, tmplDir string, cacheSize int, timeout time.Duration) error { siteCfg := &core.Site{Name: name, BaseURL: base, Root: root} if err := core.Start(siteCfg); err != nil { return err } var tfs fs.FS var err error if tmplDir != "" { tfs = os.DirFS(tmplDir) } else if tfs, err = fs.Sub(templateFS, "templates"); err != nil { return err } srv, err := core.NewServer(siteCfg, site.Routes(), tfs, cacheSize) if err != nil { return err } srv.TemplateDir = tmplDir srv.Timeout = timeout static, err := fs.Sub(staticFS, "static") if err != nil { return err } mux := http.NewServeMux() mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(static)))) mux.Handle("/", srv.Handler()) slog.Info("gsg listening", "addr", addr, "repos", len(siteCfg.RepoList()), "routes", len(site.Routes()), "modules", len(core.Names())) hs := &http.Server{ Addr: addr, Handler: mux, ReadHeaderTimeout: 5 * time.Second, WriteTimeout: timeout + 5*time.Second, } return hs.ListenAndServe() }