@@ 25,7 25,7 @@
package main
import (
- "fmt"
+ "errors"
"log/slog"
"os"
"strings"
@@ 37,6 37,7 @@ import (
"go.bigb.es/auxilia/scribe"
"sourcecraft.dev/bigbes/sr-ht-core/config"
coreserver "sourcecraft.dev/bigbes/sr-ht-core/server"
+ "sourcecraft.dev/bigbes/sr-ht-ecore/instconf"
"sourcecraft.dev/bigbes/sr-ht-ecore/logging"
"sourcecraft.dev/bigbes/sr-ht-ecore/login"
@@ 173,49 174,37 @@ func main() {
// server.New), which would otherwise fatal with a terse message on a missing
// network-key or webhook key.
func validateConfig(conf ini.File) string {
- var missing []string
- require := func(section, key string) {
- if v, ok := conf.Get(section, key); !ok || strings.TrimSpace(v) == "" {
- missing = append(missing, fmt.Sprintf("[%s] %s", section, key))
- }
- }
-
- require("sr.ht", "network-key") // crypto: unified-login cookie fernet key
- require("webhooks", "private-key") // crypto: webhook signing key (shared)
- require("git.sr.ht", "repos") // bare repository root on disk
- require("meta.sr.ht", "origin") // login/logout links in the nav
- require("compare.sr.ht", "origin") // our own external origin
-
- // git.sr.ht needs at least one internal API origin candidate; without it
- // config.GetAPI panics on the first authorization request.
- apiOrigin := firstConfigured(conf, "git.sr.ht",
- "api-internal-origin", "internal-origin", "api-origin", "origin")
- if apiOrigin == "" {
- missing = append(missing,
- "[git.sr.ht] one of api-internal-origin, internal-origin, api-origin, origin")
- }
-
- if len(missing) > 0 {
+ err := instconf.Require(conf,
+ instconf.Need("sr.ht", "network-key"), // crypto: unified-login cookie fernet key
+ instconf.Need("webhooks", "private-key"), // crypto: webhook signing key (shared)
+ instconf.Need("git.sr.ht", "repos"), // bare repository root on disk
+ instconf.Need("meta.sr.ht", "origin"), // login/logout links in the nav
+ instconf.Need(service, "origin"), // our own external origin
+
+ // git.sr.ht needs at least one internal API origin candidate; without
+ // it config.GetAPI panics on the first authorization request. The
+ // ladder is asked for by name rather than written out, so this check
+ // and the lookup below cannot come to disagree about it.
+ instconf.NeedAny("git.sr.ht", instconf.APIOriginKeys()...),
+ )
+ if err != nil {
// One record and one exit, deliberately: an operator fixing a config
// wants every gap in front of them at once, and a line per missing key
- // is a restart per missing key. The keys go in as a slice attribute
- // rather than a joined string so the structured sinks keep them as a
+ // is a restart per missing key. Strings() keeps them a slice attribute
+ // rather than a joined string, so the structured sinks keep them as a
// list — the tint handler still renders them on one line.
- slog.Error("incomplete configuration", "missing", missing)
+ var missing *instconf.MissingKeysError
+ if errors.As(err, &missing) {
+ slog.Error("incomplete configuration", "missing", missing.Strings())
+ } else {
+ slog.Error("incomplete configuration", scribe.Err(err))
+ }
os.Exit(1)
}
- return apiOrigin
-}
-// firstConfigured returns the value of the first present, non-empty key in
-// section, or "" if none are set.
-func firstConfigured(conf ini.File, section string, keys ...string) string {
- for _, k := range keys {
- if v, ok := conf.Get(section, k); ok && strings.TrimSpace(v) != "" {
- return v
- }
- }
- return ""
+ // Cannot report false: NeedAny above walked the same ladder.
+ apiOrigin, _ := instconf.InternalAPIOrigin(conf, "git.sr.ht")
+ return apiOrigin
}
// resolveBind reconstructs the primary bind address server.New will use, for