indexer/config/config.go
Ricardo (XenGi) Band 63c6f4b761
Some checks failed
CI pipeline for ffd-indexer / checks (go test + golangci) (push) Failing after 26s
CI pipeline for ffd-indexer / build binaries + containers (push) Failing after 3s
CI pipeline for ffd-indexer / format (gofmt + nixfmt) (push) Failing after 3s
CI pipeline for ffd-indexer / integration (live scrape + Typesense) (push) Failing after 20s
🔥
2026-10-01 00:37:44 +02:00

102 lines
3.5 KiB
Go

// Package config holds the indexer's YAML configuration.
//
// It lives in its own package (rather than package main) so that both the
// indexer CLI and the ffd-metrics daemon can parse the same configuration
// file shape.
package config
import (
"fmt"
"os"
"strings"
)
// Forge is a single instance to scrape: its bare hostname (FQDN) and its
// forge type. The URL is used as the forge's unique identity (the HTTP scheme
// is added internally when a connection is needed; a scheme prefix in the URL
// is accepted to force the connection protocol, but the stored identity is
// always the bare hostname). Type selects the scraper: a pinned "forgejo" or
// "gitea" is recommended and skips probing; "auto" (the default, still
// experimental) runs the detector at scrape time so a backend change (e.g. a
// migration from Gitea to Forgejo) is picked up automatically. Only supported
// types are accepted.
type Forge struct {
URL string `yaml:"url"`
Type string `yaml:"type"`
}
// Forges is the list of forge instances to scrape.
type Forges []Forge
// TypesenseConfig holds the connection settings to the search engine.
// ApiKey may be inlined or supplied via ApiKeyFile (a path to a file
// containing the key, e.g. a sops/agenix secret). If both are set, the file
// takes precedence so secrets need not live in the config file.
type TypesenseConfig struct {
Host string `yaml:"host"`
ApiKey string `yaml:"api_key"`
ApiKeyFile string `yaml:"api_key_file"`
}
// MetricsConfig configures the Prometheus metrics daemon.
type MetricsConfig struct {
// Listen is the address the ffd-metrics daemon binds and serves
// /metrics on, e.g. "127.0.0.1:9090".
Listen string `yaml:"listen"`
}
// Config is the top-level configuration file.
type Config struct {
DbPath string `yaml:"db_path"`
Typesense TypesenseConfig `yaml:"typesense"`
Metrics MetricsConfig `yaml:"metrics"`
Forges Forges `yaml:"forges"`
}
// Supported forge type values accepted in the config. "auto" (the default)
// triggers detection at scrape time; the rest pin a specific scraper.
const (
ForgeTypeAuto = "auto"
ForgeTypeForgejo = "forgejo"
ForgeTypeGitea = "gitea"
)
// SupportedForgeTypes are the accepted values for Forge.Type, including the
// sentinel "auto" that triggers detection at scrape time.
var SupportedForgeTypes = map[string]bool{
"": true, // default to "auto"
ForgeTypeAuto: true,
ForgeTypeForgejo: true,
ForgeTypeGitea: true,
}
// Validate normalizes a Forge's Type (empty means "auto") and rejects
// unsupported values.
func (f *Forge) Validate() error {
if f.URL == "" {
return fmt.Errorf("forge entry missing url")
}
if f.Type == "" {
f.Type = ForgeTypeAuto
}
if !SupportedForgeTypes[f.Type] {
return fmt.Errorf("unsupported forge type %q for %s (want %s, %s or %s)", f.Type, f.URL, ForgeTypeAuto, ForgeTypeForgejo, ForgeTypeGitea)
}
return nil
}
// ResolveAPIKey returns the effective Typesense API key. If ApiKeyFile is
// set it is read (taking precedence over the inline ApiKey), and its contents
// are trimmed of surrounding whitespace/newline so a plain `cat` secret file
// works. An empty key (from either source) is returned as "" for the caller
// to treat as "not configured".
func (tc TypesenseConfig) ResolveAPIKey() (string, error) {
if tc.ApiKeyFile == "" {
return tc.ApiKey, nil
}
b, err := os.ReadFile(tc.ApiKeyFile)
if err != nil {
return "", fmt.Errorf("read typesense api_key_file %q: %w", tc.ApiKeyFile, err)
}
return strings.TrimSpace(string(b)), nil
}