pcmt/config/config.go
leo 3a2f85f683
All checks were successful
continuous-integration/drone/push Build is passing
feat: add license headers (+spdx id)
2023-05-20 20:15:57 +02:00

147 lines
3.4 KiB
Go

// Copyright 2023 wanderer <a_mirre at utb dot cz>
// SPDX-License-Identifier: AGPL-3.0-only
package config
import (
"context"
"fmt"
"os"
"os/exec"
"time"
"git.dotya.ml/mirre-mt/pcmt/slogging"
"github.com/philandstuff/dhall-golang/v6"
"golang.org/x/exp/slog"
)
type session struct {
CookieName string
CookieSecret string
}
type Config struct {
Host string
Port int
AppName string
LiveMode bool
DevelMode bool
Session session
Registration struct{ Allowed bool }
Logger struct {
Json bool //nolint:revive
Fmt string
}
}
const schemaCompatibility = "0.0.0" //nolint:unused
var log slogging.Slogger
func Load(conf string, isPath bool) (*Config, error) {
var config Config
var err error
slogger := slogging.Logger()
// initialise if not already initialised.
if slogger == nil {
slogger = slogging.Init(true)
}
// have a local copy.
log = *slogger
// add attr to all statements made with the local copy.
log.Logger = log.Logger.With(
slog.Group("pcmt extra", slog.String("module", "config")),
)
switch {
case isPath:
log.Debug("config from file")
err = dhall.UnmarshalFile(conf, &config)
case !isPath:
log.Debug("config raw from cmdline")
err = dhall.Unmarshal([]byte(conf), &config)
}
if !config.Logger.Json {
slogger = slogging.Init(false)
log.Logger = slogger.Logger.With(
slog.Group("pcmt extra", slog.String("module", "config")),
)
}
if config.DevelMode && os.Getenv("PCMT_DEVEL") != "False" {
log.Debug("set DEBUG level based on config value")
slogger = slogging.SetLevel(slogging.LevelDebug)
log.Logger = slogger.Logger.With(
slog.Group("pcmt extra", slog.String("module", "config")),
)
log.Debugf("parsed config: %+v", config)
if dhallCmdExists() {
_ = prettyPrintConfig(conf, isPath)
}
}
// only return now (if err), after we've had the chance to print the loaded
// config, as would be the case if the config *did* adhere to the schema imported
// but app's Config type definition above didn't - "don't know how to
// decode <nil> into ..." is the most likely outcome in that case and it
// could be the product of (at least) three things:
// * completely wrong config not following the schema
// * config is following a newer schema than the app supports
// * config is following an older schema than the app supports
//
// NOTE: for the most part the only thing checked above would be adherence
// to config schema (apart from *some* value validation).
if err != nil {
return nil, err
}
return &config, nil
}
func prettyPrintConfig(conf string, isPath bool) error {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
var cmd *exec.Cmd
if isPath {
cmd = exec.CommandContext(ctx, "/bin/sh", "-c", "dhall --file "+conf) //nolint:gosec
} else {
cmd = exec.CommandContext(ctx, "/bin/sh", "-c", "dhall <<< \""+conf+"\"") //nolint:gosec
}
output, err := cmd.CombinedOutput()
if err != nil {
log.Debug("could not pretty-print config", "error", err)
return err
}
if isPath {
fmt.Fprintln(os.Stderr, "\n"+conf+":\n"+string(output))
} else {
fmt.Fprintln(os.Stderr, "\nconfig:\n"+string(output))
}
return nil
}
func dhallCmdExists() bool {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if err := exec.CommandContext(ctx, "/bin/sh", "-c", "command -v dhall").Run(); err != nil {
log.Debug("no command dhall")
return false
}
return true
}