main.go
// socle is a read-only web host for local git repositories:
// browsing of committed state at refs, and anonymous fetch over smart HTTP.
// Nothing in it can write to a repo.
package main
import (
"flag"
"log"
"net/http"
"os"
"time"
"vitrine/display"
)
func main() {
configPath := flag.String("config", "", "path to the socle config file (required; there is no default location)")
listen := flag.String("listen", ":8080", "listen address")
themeSeed := flag.String("theme-seed", "", "theme name override (default: first dot-segment of the hostname)")
flag.Parse()
if *configPath == "" {
log.Fatal("socle: the -config flag is required")
}
seed := *themeSeed
if seed == "" {
hostname, err := os.Hostname()
if err != nil {
log.Fatalf("socle: cannot determine hostname for theming (use -theme-seed to bypass): %v", err)
}
seed = display.HostLabel(hostname)
}
cfg, err := loadConfig(*configPath)
if err != nil {
log.Fatal("socle: ", err)
}
repos, warnings := openAll(cfg)
for _, w := range warnings {
log.Print("socle: ", w)
}
srv := newServer(repos, display.NewTheme(seed))
// Config changes apply live: reloads are fail-stable
// (any problem logs loudly and the old repo set keeps serving),
// while startup above stays fail-closed as ever.
watchConfig(*configPath, 5*time.Second, func() {
reloadConfig(srv, *configPath)
})
log.Printf("socle: serving %d repos on %s", len(repos), *listen)
log.Fatal(http.ListenAndServe(*listen, srv))
}
// reloadConfig loads the config file and swaps the server's repo set to it.
// Fail-stable, unlike startup: a config that cannot be loaded (parse or validation
// failure) or that yields not one openable repo is rejected loudly and the previous
// set keeps serving -- a showcase must not go dark from one typo mid-edit.
// Per-repo open failures are the same warning-and-skip as at startup.
func reloadConfig(srv *server, path string) {
cfg, err := loadConfig(path)
if err != nil {
log.Printf("socle: config reload REJECTED, keeping previous repos: %v", err)
return
}
repos, warnings := openAll(cfg)
for _, w := range warnings {
log.Print("socle: ", w)
}
if len(repos) == 0 {
log.Printf("socle: config reload REJECTED, keeping previous repos: no configured repo could be opened")
return
}
srv.setRepos(repos)
log.Printf("socle: config reloaded: %d repos", len(repos))
}
main.go · 2.3 KiB · 4eed84cb53