repositories / bugabinga.net
bugabinga.net
personal infrastructure for bugabinga!
owned by admin
services/luci/internal/config/config.go
Rawpackage config
import (
"fmt"
"os"
"strconv"
"strings"
"time"
)
type Config struct {
DataDir string
RepoRoots []string
HTTPAddr string
BaseURL string
InboxDir string
PodmanSocket string
SSHAddr string
SSHAuthorizedKeys string
ThemeCSS string
DefaultTimeout time.Duration
DefaultMemory string
DefaultCPUs string
CacheMaxVersions int
CacheMaxBytes int64
LogLevel string
PublishRegistryAuthFile string
PublishPkgRoot string
PublishSiteRoot string
}
func Load() (Config, error) {
var cfg Config
var err error
if cfg.DataDir, err = required("LUCI_DATA_DIR"); err != nil {
return Config{}, err
}
repoRoots, err := required("LUCI_REPO_ROOTS")
if err != nil {
return Config{}, err
}
cfg.RepoRoots = splitList(repoRoots)
if cfg.HTTPAddr, err = required("LUCI_HTTP_ADDR"); err != nil {
return Config{}, err
}
if cfg.BaseURL, err = required("LUCI_BASE_URL"); err != nil {
return Config{}, err
}
if cfg.InboxDir, err = required("LUCI_INBOX_DIR"); err != nil {
return Config{}, err
}
if cfg.PodmanSocket, err = required("LUCI_PODMAN_SOCKET"); err != nil {
return Config{}, err
}
cfg.SSHAddr = optional("LUCI_SSH_ADDR", "")
cfg.SSHAuthorizedKeys = optional("LUCI_SSH_AUTHORIZED_KEYS", "")
cfg.ThemeCSS = optional("LUCI_THEME_CSS", "")
cfg.DefaultMemory = optional("LUCI_DEFAULT_MEMORY", "2g")
cfg.DefaultCPUs = optional("LUCI_DEFAULT_CPUS", "2")
cfg.LogLevel = optional("LUCI_LOG_LEVEL", "info")
cfg.PublishRegistryAuthFile = optional("LUCI_PUBLISH_REGISTRY_AUTH_FILE", "")
cfg.PublishPkgRoot = optional("LUCI_PUBLISH_PKG_ROOT", "")
cfg.PublishSiteRoot = optional("LUCI_PUBLISH_SITE_ROOT", "")
timeoutText := optional("LUCI_DEFAULT_TIMEOUT", "20m")
cfg.DefaultTimeout, err = time.ParseDuration(timeoutText)
if err != nil {
return Config{}, fmt.Errorf("invalid LUCI_DEFAULT_TIMEOUT: %w", err)
}
if versionsText := optional("LUCI_CACHE_MAX_VERSIONS", "3"); versionsText != "" {
cfg.CacheMaxVersions, err = strconv.Atoi(versionsText)
if err != nil || cfg.CacheMaxVersions < 1 {
return Config{}, fmt.Errorf("invalid LUCI_CACHE_MAX_VERSIONS: %q", versionsText)
}
}
if bytesText := optional("LUCI_CACHE_MAX_BYTES", "2GiB"); bytesText != "" {
cfg.CacheMaxBytes, err = parseBytes(bytesText)
if err != nil {
return Config{}, fmt.Errorf("invalid LUCI_CACHE_MAX_BYTES: %w", err)
}
}
return cfg, nil
}
// parseBytes accepts a plain byte count or a binary suffix (KiB, MiB, GiB,
// TiB, and their one-letter forms).
func parseBytes(text string) (int64, error) {
units := map[string]int64{"": 1, "k": 1 << 10, "m": 1 << 20, "g": 1 << 30, "t": 1 << 40}
trimmed := strings.ToLower(strings.TrimSpace(text))
if trimmed == "" {
return 0, fmt.Errorf("invalid byte size %q", text)
}
for _, suffix := range []string{"kib", "mib", "gib", "tib"} {
if strings.HasSuffix(trimmed, suffix) {
trimmed = strings.TrimSuffix(trimmed, suffix) + suffix[:1]
break
}
}
trimmed = strings.TrimSuffix(trimmed, "b")
unit := ""
if last := trimmed[len(trimmed)-1]; last >= 'a' && last <= 'z' {
if _, ok := units[string(last)]; ok {
unit = string(last)
trimmed = trimmed[:len(trimmed)-1]
}
}
value, err := strconv.ParseInt(trimmed, 10, 64)
if err != nil || value < 0 || value > (1<<63-1)/units[unit] {
return 0, fmt.Errorf("invalid byte size %q", text)
}
return value * units[unit], nil
}
func required(name string) (string, error) {
value, ok := os.LookupEnv(name)
if !ok || strings.TrimSpace(value) == "" {
return "", fmt.Errorf("missing required env %s", name)
}
return value, nil
}
func optional(name string, fallback string) string {
value, ok := os.LookupEnv(name)
if !ok || strings.TrimSpace(value) == "" {
return fallback
}
return value
}
func splitList(value string) []string {
parts := strings.Split(value, ":")
out := make([]string, 0, len(parts))
for _, part := range parts {
part = strings.TrimSpace(part)
if part != "" {
out = append(out, part)
}
}
return out
}