main.go
// Command vitrine is a tiny read-only web viewer for local source trees.
//
// Each configured exhibit is served under its name as a URL prefix,
// e.g. "/moonshot-1/crates/iri/filetests/verify-errors.md".
// The default view is rendered (markdown becomes HTML, directories become listings);
// appending "?fmt=raw" serves the file bytes directly.
//
// Exhibits come from a YAML config file given via the required -config flag;
// see config.go for the schema and README.md for examples.
package main
import (
"flag"
"log"
"net/http"
"os"
"time"
)
func main() {
addr := flag.String("addr", ":8000", "listen address")
configPath := flag.String("config", "", "path to config file (required)")
themeSeed := flag.String("theme-seed", "", "theme name override (default: first dot-segment of the hostname)")
flag.Parse()
if *configPath == "" {
log.Fatal("the -config flag is required; there is no default config location")
}
seed := *themeSeed
if seed == "" {
hostname, err := os.Hostname()
if err != nil {
log.Fatalf("cannot determine hostname for theming (use -theme-seed to bypass): %v", err)
}
seed = hostLabel(hostname)
}
cfg, err := loadConfig(*configPath)
if err != nil {
log.Fatal(err)
}
srv, err := newServer(cfg.Exhibits, newTheme(seed))
if err != nil {
log.Fatal(err)
}
// Config changes apply live: reloads are fail-stable
// (a bad config logs loudly and the old exhibits keep serving),
// in contrast to the fail-loud startup above.
watchConfig(*configPath, 5*time.Second, func() {
reloadConfig(srv, *configPath)
})
log.Printf("vitrine listening on %s", *addr)
log.Fatal(http.ListenAndServe(*addr, srv))
}
// reloadConfig loads the config file and swaps the server's exhibits to it.
// Any failure (parse, validation, unopenable exhibit dir) logs loudly
// and leaves the previously served exhibits untouched:
// a fleet of vitrines must not go dark from one typo mid-edit.
func reloadConfig(srv *server, path string) {
cfg, err := loadConfig(path)
if err != nil {
log.Printf("config reload REJECTED, keeping previous exhibits: %v", err)
return
}
if err := srv.setExhibits(cfg.Exhibits); err != nil {
log.Printf("config reload REJECTED, keeping previous exhibits: %v", err)
return
}
log.Printf("config reloaded: %d exhibits", len(cfg.Exhibits))
}
main.go · 2.3 KiB · 8921cae64f