123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 | package render
import (
"errors"
"fmt"
"io"
"os"
"strings"
"code.bruce-hill.com/repo-renderer/internal/config"
"code.bruce-hill.com/repo-renderer/internal/gitx"
"code.bruce-hill.com/repo-renderer/internal/httpd"
)
// Check validates the configuration (loaded from cfgPath), lexers,
// templates and the directories and tools a render needs, reporting on w.
// Problems that would break a render are returned; the rest are warnings.
func (s *Site) Check(w io.Writer, cfgPath string) error {
var problems []error
// Run git under the same sandbox the renderer uses, so a missing
// sandbox entry shows up here and not in the first post-receive.
if err := s.restrict(); err != nil {
problems = append(problems, fmt.Errorf("sandbox: %w", err))
}
v, err := gitx.Version()
if err != nil {
problems = append(problems, fmt.Errorf("git does not run (missing from the sandbox?): %w", err))
} else {
fmt.Fprintln(w, "git:", v)
}
probe := gitx.Repo{Dir: s.Lay.GitBase}
if _, err := probe.Run("rev-parse", "--is-bare-repository"); err != nil && !strings.Contains(err.Error(), "not a git repository") {
problems = append(problems, fmt.Errorf("git cannot run inside %s: %w", s.Lay.GitBase, err))
}
fmt.Fprintln(w, "config:", cfgPath)
lay := s.Lay
logPath := s.Cfg.Log
if logPath == "" {
logPath = "(stderr)"
}
for _, kv := range [][2]string{
{"repositories", lay.GitBase}, {"site", lay.Htdocs}, {"spool", lay.QueueDir},
{"state", lay.State}, {"log", logPath}, {"lexers", strings.Join(s.Cfg.LexerDirs, " ")},
} {
fmt.Fprintf(w, "%s: %s\n", kv[0], kv[1])
}
fmt.Fprintln(w, "host:", s.Cfg.Host)
fmt.Fprintln(w, "style:", s.HL.StyleName())
fmt.Fprintln(w, "template hash:", s.TmplHash)
for _, name := range s.Cfg.LexerNames() {
lx := s.HL.ByName(name)
if s.HL.IsPlain(lx) {
problems = append(problems, fmt.Errorf("lexer %q resolves to plain text", name))
} else {
fmt.Fprintf(w, "lexer %s: ok (%v)\n", name, s.Cfg.Lexers[name].Globs)
}
}
for _, d := range []string{lay.GitBase, lay.Htdocs, lay.QueueDir, lay.State} {
if st, err := os.Stat(d); err != nil || !st.IsDir() {
fmt.Fprintf(w, "warning: %s does not exist yet\n", d)
}
}
if l := s.Cfg.Log; l != "" && l != config.LogSyslog {
if f, err := os.OpenFile(l, os.O_WRONLY|os.O_APPEND, 0); err != nil {
fmt.Fprintf(w, "warning: cannot write the log %s: %v\n", l, err)
} else {
_ = f.Close() // opened only to see that it could be
}
}
repos, err := s.Repos()
if err != nil {
problems = append(problems, err)
}
have := map[string]bool{}
for _, r := range repos {
have[r] = true
}
for _, sec := range s.Cfg.Sections {
for _, r := range sec.Repos {
if !have[r] {
fmt.Fprintf(w, "warning: section %q lists %q, which is not under %s\n", sec.Title, r, s.Lay.GitBase)
}
}
}
var private []string
for _, r := range repos {
if s.Cfg.Private(r) {
private = append(private, r)
}
}
if len(private) > 0 {
where := "not on the site: set private_htdocs to serve them behind a password"
if s.priv != nil {
where = "on the private site, " + s.Cfg.BaseURL() + httpd.PrivatePrefix + "/"
}
fmt.Fprintf(w, "private (in no section, so %s): %s\n", where, strings.Join(private, ", "))
}
mirrors := false
for r, rc := range s.Cfg.Repos {
if !have[r] {
fmt.Fprintf(w, "warning: repos.%s configured but not under %s\n", r, s.Lay.GitBase)
}
mirrors = mirrors || len(rc.Mirrors) > 0
}
if mirrors {
if _, err := os.Stat("/etc/ssh/ssh_known_hosts"); err != nil {
fmt.Fprintf(w, "warning: mirroring is configured but ssh knows no host keys; as root, run this for each mirror's host\n ssh-keyscan HOST >> /etc/ssh/ssh_known_hosts\n")
}
}
if len(problems) > 0 {
return errors.Join(problems...)
}
fmt.Fprintln(w, "ok")
return nil
}
|