watchdog/cmd/watchdog/main.go
NotAShelf 9efabc8f76
cmd: infer version from version.json; add version flag
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I0f429ef3345444c0c4bedbc80ee3d5e06a6a6964
2026-03-07 10:04:07 +03:00

140 lines
3.3 KiB
Go

package watchdog
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"notashelf.dev/watchdog/internal/config"
)
var (
cfgFile string
cfg *config.Config
version string
commit string
buildDate string
)
type versionInfo struct {
Version string `json:"version"`
Commit string `json:"commit"`
BuildDate string `json:"buildDate"`
}
func getVersion() string {
if version != "" {
return version
}
data, err := os.ReadFile("version.json")
if err != nil {
return "dev"
}
var v versionInfo
if err := json.Unmarshal(data, &v); err != nil {
return "dev"
}
if v.Version != "" {
return v.Version
}
return "dev"
}
func getCommit() string {
if commit != "" {
return commit
}
data, err := os.ReadFile("version.json")
if err != nil {
return "none"
}
var v versionInfo
if err := json.Unmarshal(data, &v); err != nil {
return "none"
}
if v.Commit != "" {
return v.Commit
}
return "none"
}
var rootCmd = &cobra.Command{
Use: "watchdog",
Short: "Privacy-first web analytics with Prometheus metrics",
Long: `Watchdog is a lightweight, privacy-first analytics system that aggregates web traffic data.`,
Version: getVersion() + "\ncommit: " + getCommit(),
RunE: func(cmd *cobra.Command, args []string) error {
return Run(cfg)
},
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().
StringVar(&cfgFile, "config", "", "config file (default: config.yaml)")
rootCmd.PersistentFlags().String("listen-addr", "", "server listen address (overrides config)")
rootCmd.PersistentFlags().String("metrics-path", "", "metrics endpoint path (overrides config)")
rootCmd.PersistentFlags().
String("ingestion-path", "", "ingestion endpoint path (overrides config)")
// Bind flags to viper
viper.BindPFlag("server.listen_addr", rootCmd.PersistentFlags().Lookup("listen-addr"))
viper.BindPFlag("server.metrics_path", rootCmd.PersistentFlags().Lookup("metrics-path"))
viper.BindPFlag("server.ingestion_path", rootCmd.PersistentFlags().Lookup("ingestion-path"))
}
func initConfig() {
if cfgFile != "" {
// Use config file from the flag
viper.SetConfigFile(cfgFile)
} else {
// Search for config in current directory
viper.AddConfigPath(".")
viper.AddConfigPath("/etc/watchdog")
viper.SetConfigName("config")
viper.SetConfigType("yaml")
}
// Environment variables
viper.SetEnvPrefix("WATCHDOG")
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// Read config file
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
fmt.Fprintf(
os.Stderr,
"Config file not found, using defaults and environment variables\n",
)
} else {
fmt.Fprintf(os.Stderr, "Error reading config file: %v\n", err)
os.Exit(1)
}
}
// Unmarshal into config struct
cfg = &config.Config{}
if err := viper.Unmarshal(cfg); err != nil {
fmt.Fprintf(os.Stderr, "Failed to parse config: %v\n", err)
os.Exit(1)
}
// Validate config
if err := cfg.Validate(); err != nil {
fmt.Fprintf(os.Stderr, "Config validation failed: %v\n", err)
os.Exit(1)
}
}
func Main(v, c, bd string) {
version, commit, buildDate = v, c, bd
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}