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
102 lines
3.5 KiB
Go
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
|
|
}
|