Story #324: Consolidate configuration into Viper Config struct; make Lidarr optional - #352
Conversation
|
Verdict: CHANGES NEEDED — one real defect (the Findings1. MEDIUM (blocking):
|
Fixes from independent review of PR #352: - BLOCKING: add config.LoadDatabase() — same Viper setup as Load() (SPOTTER_* prefix, key replacer, defaults) with driver validation and driver-specific default DSNs, but scoped to database config only. cmd/admin rotate-key now uses it, so the tool runs standalone with no server env vars (SPEC key-rotation Scenario 1) while still honoring ADR-0009's no-hand-rolled-os.Getenv rule. Load() refactored to share newViper() and validateAndDefaultDatabase(). LoadDatabase also preserves the pre-consolidation empty-env-var fallbacks (Viper treats empty strings as set). - Non-numeric SPOTTER_MAX_CONCURRENT_JOBS now yields a clear validation error ("max_concurrent_jobs must be an integer ...") instead of a raw mapstructure decode failure. - GetShutdownTimeout and GetSyncHistoryLookback treat non-positive durations as invalid and fall back to their defaults (prevents zero-grace shutdowns and future-dated sync watermarks). - MetadataEnricherOrder logs a WARN when filtering unconfigured enrichers empties the order (metadata enrichment would silently no-op). - server.base_url is normalized (trailing slashes trimmed) for the email-links consumer. - CONSTRAINT comments explain why shutdown_timeout/max_concurrent_jobs are top-level keys (published env var names from SPEC graceful-shutdown must remain stable under the dot-to-underscore replacer). - Tests: TestLoadDatabase (standalone, defaults, driver default source, invalid driver), non-numeric/negative max_concurrent_jobs, negative/ zero shutdown timeout, negative history lookback, trailing-slash base_url, empty-after-filter enricher order. Implements #324 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Addressed the review findings in d44a92f: BLOCKING — standalone Strongly recommended items, all done:
Optional nits:
🤖 Posted on behalf of |
|
Verdict: LGTM — delta re-review of d44a92f, all findings resolved. Independently re-verified on a fresh detached checkout:
Good to merge. 🤖 Posted on behalf of |
Consolidates all application configuration into the Viper-backed *config.Config struct per ADR-0009 and makes Lidarr configuration fully optional: - Move SPOTTER_SHUTDOWN_TIMEOUT and SPOTTER_MAX_CONCURRENT_JOBS out of raw os.Getenv in cmd/server/main.go into config keys shutdown_timeout (default 30s) and max_concurrent_jobs (default 10), with GetShutdownTimeout()/GetMaxConcurrentJobs() helpers that preserve the previous fallback-on-invalid behavior (SPEC graceful-shutdown REQ-TMO-005, REQ-SEM-002). - cmd/admin now uses config.Load() for database driver/DSN instead of hand-rolled os.Getenv with duplicated SQLite defaults; the --db flag still overrides the configured DSN (ADR-0023, ADR-0009). - Lidarr is optional: config.Load() no longer hard-fails on empty lidarr.base_url/lidarr.api_key (it only rejects partial configuration), IsLidarrEnabled() gates both the Lidarr enricher registration and the background submitter, and MetadataEnricherOrder() filters "lidarr" out when unconfigured so shipped docker-compose examples start without Lidarr env vars (SPEC-0014 compose scenarios, SPEC-0017 REQ "Background Submitter Goroutine"). - Add server.base_url key for sync-failure email links (ADR-0026, SPEC-0015; consumed by the email-content story). - Add sync.history_lookback key (default 720h) and use it in the Syncer's initial history fetch instead of syncing from the Unix epoch (SPEC listen-playlist-sync REQ-SYNC-020). - Update .env.example and the docs-site configuration reference for all new/moved keys. - Add config tests covering SPOTTER_* env binding for the new keys, Lidarr-optional/partial/enabled validation, and enricher-order filtering. Acceptance: no application-config os.Getenv remains outside internal/config; server startup without Lidarr env vars reaches DB connection and logs the enricher/submitter skip paths. Implements #324 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fixes from independent review of PR #352: - BLOCKING: add config.LoadDatabase() — same Viper setup as Load() (SPOTTER_* prefix, key replacer, defaults) with driver validation and driver-specific default DSNs, but scoped to database config only. cmd/admin rotate-key now uses it, so the tool runs standalone with no server env vars (SPEC key-rotation Scenario 1) while still honoring ADR-0009's no-hand-rolled-os.Getenv rule. Load() refactored to share newViper() and validateAndDefaultDatabase(). LoadDatabase also preserves the pre-consolidation empty-env-var fallbacks (Viper treats empty strings as set). - Non-numeric SPOTTER_MAX_CONCURRENT_JOBS now yields a clear validation error ("max_concurrent_jobs must be an integer ...") instead of a raw mapstructure decode failure. - GetShutdownTimeout and GetSyncHistoryLookback treat non-positive durations as invalid and fall back to their defaults (prevents zero-grace shutdowns and future-dated sync watermarks). - MetadataEnricherOrder logs a WARN when filtering unconfigured enrichers empties the order (metadata enrichment would silently no-op). - server.base_url is normalized (trailing slashes trimmed) for the email-links consumer. - CONSTRAINT comments explain why shutdown_timeout/max_concurrent_jobs are top-level keys (published env var names from SPEC graceful-shutdown must remain stable under the dot-to-underscore replacer). - Tests: TestLoadDatabase (standalone, defaults, driver default source, invalid driver), non-numeric/negative max_concurrent_jobs, negative/ zero shutdown timeout, negative history lookback, trailing-slash base_url, empty-after-filter enricher order. Implements #324 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d44a92f to
022beff
Compare
What / Why
Implements story #324 (part of epic #323, foundation story). ADR-0009's confirmation clause — no direct
os.Getenv()for application configuration outsideconfig.Load()— was violated in both binaries, and config validation made Lidarr accidentally mandatory, so the shipped docker-compose examples could not start and SPEC-0017's "submitter SHALL only start if Lidarr is configured" branch was dead code.Changes
internal/config/config.go,cmd/server/main.go):SPOTTER_SHUTDOWN_TIMEOUT→shutdown_timeout(default30s),SPOTTER_MAX_CONCURRENT_JOBS→max_concurrent_jobs(default10). NewGetShutdownTimeout()/GetMaxConcurrentJobs()helpers preserve the old fallback-on-invalid behavior. Env var names are unchanged (Viper key replacer maps them 1:1). Governing: ADR-0009, SPEC graceful-shutdown REQ-TMO-005 / REQ-SEM-002.cmd/adminusesconfig.Load()for database driver/DSN instead of hand-rolledos.Getenv+ duplicated SQLite defaults;--dbflag still overrides. Diff kept surgical to themain()config-loading region (key-rotation logic untouched for Replace IsEncrypted heuristic with explicit ciphertext marker (auth-row bricking) #335). Governing: ADR-0023, ADR-0009.lidarr.base_url/lidarr.api_key— it only rejects partial configuration (one set without the other). NewConfig.IsLidarrEnabled()gates the Lidarr enricher registration and the background submitter incmd/server/main.go, andMetadataEnricherOrder()filterslidarrout when unconfigured (avoids per-run "unknown enricher type" warnings without touchinginternal/services/metadata.go, owned by Fix enrichment batch starvation, image collisions, best-image selection #343). Governing: SPEC-0014 compose scenarios, SPEC-0017 REQ "Background Submitter Goroutine".server.base_urlkey (default empty) for sync-failure email links; consumed by the email-content story in EPIC: Configuration & Notifications #323. Governing: ADR-0026, SPEC-0015.sync.history_lookbackkey (default720h) wired intointernal/services/sync.go— first-time history fetch now looks back 30 days instead of syncing from the Unix epoch. Governing: SPEC listen-playlist-sync REQ-SYNC-020..env.exampleanddocs-site/docs/getting-started/configuration.mdupdated for all new/moved keys, including the Lidarr both-or-neither rule.Test Evidence
make testpasses: 27 packages ok, 0 failures (includes codegen).TestLidarrOptional,TestLidarrEnabledWhenFullyConfigured,TestLidarrPartialConfigRejected,TestShutdownTimeout(default/override/invalid),TestMaxConcurrentJobs(default/override/non-positive),TestServerBaseURL,TestSyncHistoryLookback(default/override/invalid),TestMetadataEnricherOrderExcludesLidarrWhenUnconfigured— provingSPOTTER_*env vars still bind through Viper.grep -rn "os.Getenv" cmd/ internal/ --include='*.go'returns only comments — zero application-config reads outsideinternal/config.gofmt -l cmd internalis empty;go vetclean.failed to connect to database ... connection refused) — the SPEC-0014 compose "MUST start" gate.lidarr enricher disabled (lidarr not configured)andlidarr queue submitter disabled (lidarr not configured), thenstarting server.Notes for Reviewers
cmd/admin rotate-keynow requires the full server config env (Navidrome URL, OpenAI key, encryption key, JWT secret) sinceconfig.Load()validates required fields. In deployed environments the admin tool runs with the server's env so this holds; flagging in case a standalone-DSN-only usage matters.Closes #324
Part of epic #323. Governing artifacts: ADR-0009 (Viper configuration), ADR-0023, ADR-0026, ADR-0015, SPEC-0014 (multi-database-support), SPEC-0015 (sync-failure-email-notifications), SPEC-0017 REQ "Background Submitter Goroutine", SPEC graceful-shutdown REQ-TMO-005 / REQ-SEM-002, SPEC listen-playlist-sync REQ-SYNC-020.
🤖 Posted on behalf of
@joestumpby Claude.