* feat: implement centralized configuration service using viper This commit introduces a centralized configuration service in `pkg/config` to reduce code duplication and provide a unified way to manage configuration across the project. Key changes: - Refactored `pkg/config` to use `github.com/spf13/viper` as the backend. - Implemented `core.Config` interface with support for layered resolution (defaults, files, environment variables). - Added `LoadFile` to support merging multiple configuration files, with automatic type detection for YAML and .env files. - Migrated `pkg/agentic`, `pkg/devops`, `pkg/build`, and `pkg/release` to use the new centralized service. - Added `mapstructure` tags to configuration structs to support viper unmarshaling. - Added comprehensive tests for the new configuration service features. This addresses the recommendations from the Architecture & Design Pattern Audit. * feat: implement centralized configuration service and address security alerts - Introduced centralized configuration service in `pkg/config` using `viper`. - Migrated major packages (`agentic`, `devops`, `build`, `release`) to the new service. - Resolved merge conflicts with `dev` branch. - Addressed CodeQL security alert by making UniFi TLS verification configurable. - Fixed `go.mod` to ensure it is tidy and consistent with direct dependencies. - Updated UniFi CLI to support TLS verification configuration.
82 lines
2 KiB
Go
82 lines
2 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
|
|
core "github.com/host-uk/core/pkg/framework/core"
|
|
"github.com/host-uk/core/pkg/io"
|
|
)
|
|
|
|
// Service wraps Config as a framework service with lifecycle support.
|
|
type Service struct {
|
|
*core.ServiceRuntime[ServiceOptions]
|
|
config *Config
|
|
}
|
|
|
|
// ServiceOptions holds configuration for the config service.
|
|
type ServiceOptions struct {
|
|
// Path overrides the default config file path.
|
|
Path string
|
|
// Medium overrides the default storage medium.
|
|
Medium io.Medium
|
|
}
|
|
|
|
// NewConfigService creates a new config service factory for the Core framework.
|
|
// Register it with core.WithService(config.NewConfigService).
|
|
func NewConfigService(c *core.Core) (any, error) {
|
|
svc := &Service{
|
|
ServiceRuntime: core.NewServiceRuntime(c, ServiceOptions{}),
|
|
}
|
|
return svc, nil
|
|
}
|
|
|
|
// OnStartup loads the configuration file during application startup.
|
|
func (s *Service) OnStartup(_ context.Context) error {
|
|
opts := s.Opts()
|
|
|
|
var configOpts []Option
|
|
if opts.Path != "" {
|
|
configOpts = append(configOpts, WithPath(opts.Path))
|
|
}
|
|
if opts.Medium != nil {
|
|
configOpts = append(configOpts, WithMedium(opts.Medium))
|
|
}
|
|
|
|
cfg, err := New(configOpts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s.config = cfg
|
|
return nil
|
|
}
|
|
|
|
// Get retrieves a configuration value by key.
|
|
func (s *Service) Get(key string, out any) error {
|
|
if s.config == nil {
|
|
return core.E("config.Service.Get", "config not loaded", nil)
|
|
}
|
|
return s.config.Get(key, out)
|
|
}
|
|
|
|
// Set stores a configuration value by key.
|
|
func (s *Service) Set(key string, v any) error {
|
|
if s.config == nil {
|
|
return core.E("config.Service.Set", "config not loaded", nil)
|
|
}
|
|
return s.config.Set(key, v)
|
|
}
|
|
|
|
// LoadFile merges a configuration file into the central configuration.
|
|
func (s *Service) LoadFile(m io.Medium, path string) error {
|
|
if s.config == nil {
|
|
return core.E("config.Service.LoadFile", "config not loaded", nil)
|
|
}
|
|
return s.config.LoadFile(m, path)
|
|
}
|
|
|
|
// Ensure Service implements core.Config and Startable at compile time.
|
|
var (
|
|
_ core.Config = (*Service)(nil)
|
|
_ core.Startable = (*Service)(nil)
|
|
)
|