chore: update golangci-lint to v2.12.2 with canonical config
All checks were successful
check / check (push) Successful in 2m3s

Replace .golangci.yml with the canonical v2-schema config
(default: all minus six disabled linters, lll 88, tests included)
and bump every golangci-lint pin to v2.12.2:

- Dockerfile: golangci/golangci-lint:v2.12.2-alpine (hash-pinned)
- script/bootstrap: GOLANGCI_LINT_VERSION 2.12.2 with new
  linux-amd64/arm64 release-archive sha256 pins

Fix all 747 findings the stricter config surfaces, with no behavior
changes: t.Parallel() throughout the test suite, static sentinel
errors and errors.Is comparisons, checked error returns, context
propagation (contextcheck/noctx), 88-column wrapping, extracted
constants and helpers for goconst/dupl/funlen/cyclop, exhaustive
switch cases replicating existing defaults, and white-box test files
renamed to *_internal_test.go for testpackage. Three
nolint:tagliatelle directives preserve the existing snake_case JSON
wire and on-disk metadata formats.
This commit is contained in:
2026-08-07 17:10:27 +00:00
parent 5d0b5f864e
commit 23506df609
55 changed files with 2584 additions and 1863 deletions

View File

@@ -2,6 +2,7 @@
package config
import (
"errors"
"fmt"
"log/slog"
"os"
@@ -24,10 +25,17 @@ const (
// Params defines dependencies for Config.
type Params struct {
fx.In
Globals *globals.Globals
Logger *logger.Logger
}
// Static validation errors.
var (
errSigningKeyRequired = errors.New("signing_key is required")
errSigningKeyTooShort = errors.New("signing_key too short")
)
// Config holds application configuration values.
type Config struct {
Debug bool
@@ -61,17 +69,19 @@ func New(_ fx.Lifecycle, params Params) (*Config, error) {
}
c := &Config{
Debug: getBool(sc, "debug", false),
MaintenanceMode: getBool(sc, "maintenance_mode", false),
Port: getInt(sc, "port", DefaultPort),
StateDir: getString(sc, "state_dir", DefaultStateDir),
SentryDSN: getString(sc, "sentry_dsn", ""),
MetricsUsername: getString(sc, "metrics.username", ""),
MetricsPassword: getString(sc, "metrics.password", ""),
SigningKey: getString(sc, "signing_key", ""),
AllowlistHosts: getStringSlice(sc, "allowlist_hosts"),
AllowHTTP: getBool(sc, "allow_http", false),
UpstreamConnectionsPerHost: getInt(sc, "upstream_connections_per_host", DefaultUpstreamConnectionsPerHost),
Debug: getBool(sc, "debug", false),
MaintenanceMode: getBool(sc, "maintenance_mode", false),
Port: getInt(sc, "port", DefaultPort),
StateDir: getString(sc, "state_dir", DefaultStateDir),
SentryDSN: getString(sc, "sentry_dsn", ""),
MetricsUsername: getString(sc, "metrics.username", ""),
MetricsPassword: getString(sc, "metrics.password", ""),
SigningKey: getString(sc, "signing_key", ""),
AllowlistHosts: getStringSlice(sc),
AllowHTTP: getBool(sc, "allow_http", false),
UpstreamConnectionsPerHost: getInt(
sc, "upstream_connections_per_host", DefaultUpstreamConnectionsPerHost,
),
}
// Build DBURL from StateDir if not explicitly set
@@ -85,7 +95,8 @@ func New(_ fx.Lifecycle, params Params) (*Config, error) {
}
// Validate required configuration
if err := c.validate(); err != nil {
err = c.validate()
if err != nil {
return nil, err
}
@@ -95,19 +106,22 @@ func New(_ fx.Lifecycle, params Params) (*Config, error) {
// validate checks that all required configuration values are set.
func (c *Config) validate() error {
if c.SigningKey == "" {
return fmt.Errorf("signing_key is required")
return errSigningKeyRequired
}
// Minimum key length for security (32 bytes = 256 bits)
const minKeyLength = 32
if len(c.SigningKey) < minKeyLength {
return fmt.Errorf("signing_key must be at least %d characters", minKeyLength)
return fmt.Errorf(
"%w: must be at least %d characters", errSigningKeyTooShort, minKeyLength,
)
}
return nil
}
// loadConfigFile loads configuration from PIXA_CONFIG_PATH env var or standard locations.
// loadConfigFile loads configuration from the PIXA_CONFIG_PATH env var
// or standard locations.
func loadConfigFile(log *slog.Logger, appName string) (*smartconfig.Config, error) {
// Check for explicit config path from environment
if envPath := os.Getenv("PIXA_CONFIG_PATH"); envPath != "" {
@@ -133,8 +147,9 @@ func loadConfigFile(log *slog.Logger, appName string) (*smartconfig.Config, erro
for _, path := range configPaths {
cleanPath := filepath.Clean(path)
//nolint:gosec // G703: paths are hardcoded config locations
if _, statErr := os.Stat(cleanPath); statErr == nil {
_, statErr := os.Stat(cleanPath)
if statErr == nil {
sc, err := smartconfig.NewFromConfigPath(path)
if err != nil {
log.Warn("failed to parse config file", "path", path, "error", err)
@@ -190,18 +205,18 @@ func getBool(sc *smartconfig.Config, key string, defaultVal bool) bool {
return val
}
func getStringSlice(sc *smartconfig.Config, key string) []string {
func getStringSlice(sc *smartconfig.Config) []string {
if sc == nil {
return nil
}
val, ok := sc.Get(key)
val, ok := sc.Get("allowlist_hosts")
if !ok || val == nil {
return nil
}
// Handle YAML list format
if slice, ok := val.([]interface{}); ok {
if slice, ok := val.([]any); ok {
result := make([]string, 0, len(slice))
for _, item := range slice {
if str, ok := item.(string); ok {