Skip to content

refactor: load configuration from env struct tags - #784

Merged
shyim merged 2 commits into
mainfrom
shyim/hearturchin
Aug 10, 2026
Merged

refactor: load configuration from env struct tags#784
shyim merged 2 commits into
mainfrom
shyim/hearturchin

Conversation

@shyim

@shyim shyim commented Aug 9, 2026

Copy link
Copy Markdown
Member

internal/config was a wall of getEnv("KEY", "default") calls, with every non-string setting hand-parsed in its own strconv block after the struct literal. This makes it declarative: each setting is a struct field with an env tag, parsed by caarlos0/env.

What changed

  • Defaults live next to the field (envDefault:"5m"), and types parse themselves — time.Duration, int, bool, float64, []string. The three post-literal parse blocks (scrape delay, rate limit, prefetch) and the getEnv/getEnvBool/parseCommaList helpers are gone.
  • The AMQP block is a nested struct with envPrefix:"QUEUE_AMQP_" instead of repeating the prefix six times.
  • Fallback chains are tags: env:"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,expand" envDefault:"${OTEL_EXPORTER_OTLP_ENDPOINT}", same for the logs endpoint and the DD_ENV/DD_VERSION aliases, replacing the nested getEnv(getEnv(...)) calls.
  • What tags can't express is split in two: normalize() for derived values (WebAuthn RP from FRONTEND_URL, mail DSN from the legacy SMTP_* vars, OtelEnabled, sampler clamp, proxy-list trimming) and validate() for range and cross-field checks, joined into one error.
  • Load() returns (*Config, error) instead of os.Exit(1) on a bad APP_SECRET. The six call sites (server, worker, 3× migrate, fixtures) return the error from their RunE.
  • telemetry.Setup takes a config struct instead of five positional strings, and OTEL_TRACES_SAMPLER_RATIO moved into config — the API now reads no environment variable outside the config package.
  • Fixes api/.env.example, which documented SMTP_FROM. Nothing reads that variable; the sender address is MAIL_FROM.

Behavior change

Unparseable or out-of-range values now fail startup instead of warning and using the default. QUEUE_AMQP_PREFETCH=many, QUEUE_AMQP_DELAYED_EXCHANGE=flase and DEPLOYMENT_SCRAPE_DELAY=-1m previously logged a warning and ran with the default; they now abort with a clear error naming the variable. That holds the line the old code was aiming at ("a typo cannot silently flip a flag that defaults to on") more strictly. Documented in SELF_HOSTING.md.

Tests

Config coverage goes from 4 to 11 tests. The existing ones were updated for the fail-fast semantics; new ones cover APP_SECRET validation, the SMTP DSN assembly, the OTLP endpoint fallbacks, trusted-proxy trimming, the sampler clamp, and the WebAuthn derivation — none of which were tested before. mise run lint and mise run test pass.

🤖 Generated with Claude Code

Every setting is now a struct field with an `env` tag parsed by
caarlos0/env, replacing the hand-rolled getEnv/getEnvBool helpers and the
per-field strconv blocks. Defaults live next to the field, the AMQP block
uses a nested envPrefix struct, and the OTLP/Datadog fallbacks are
expressed as expanding defaults instead of nested getEnv calls.

What tags cannot express is split into normalize (values derived from
other settings) and validate (range and cross-field checks).

Load now returns an error instead of calling os.Exit, and unparseable or
out-of-range values fail startup rather than silently falling back to the
default, so a typo cannot run an instance with settings nobody chose.

telemetry.Setup takes a config struct instead of five positional strings
and no longer reads OTEL_TRACES_SAMPLER_RATIO itself, so the API reads no
environment variable outside the config package.

Also fixes .env.example, which documented SMTP_FROM — a variable nothing
reads; the sender address is MAIL_FROM.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR replaces imperative environment lookup and parsing with tagged configuration structs, centralized normalization and validation, and explicit configuration-load error propagation.

  • Declares defaults, aliases, nested AMQP settings, and typed parsing through caarlos0/env.
  • Moves telemetry environment access into the shared configuration package and passes a typed telemetry configuration to server and worker setup.
  • Updates CLI call sites, tests, self-hosting documentation, and the example mail sender variable.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains in the eligible follow-up scope.

No blocking failure remains.

Important Files Changed

Filename Overview
api/internal/config/config.go Replaces imperative environment reads with typed tags, normalization, validation, and returned errors; no eligible follow-up finding was established.
api/internal/config/config_test.go Expands coverage for fail-fast parsing, derived values, aliases, SMTP assembly, proxy trimming, and typed defaults.
api/internal/telemetry/telemetry.go Changes telemetry initialization to consume a typed configuration and removes direct sampler environment parsing.
api/telemetry.go Adds the adapter translating application configuration into telemetry configuration.
api/server.go Propagates configuration-load errors and uses the typed telemetry adapter.
api/worker.go Propagates configuration-load errors while preserving the worker-specific telemetry service name.
api/migrate.go Updates all migration commands to propagate configuration-load errors.
api/fixtures.go Updates fixture loading to propagate configuration errors before initializing dependencies.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Env[Environment and .env] --> Parse[caarlos0/env Parse]
  Parse --> Normalize[normalize derived values]
  Normalize --> Validate[validate ranges and cross-field rules]
  Validate -->|valid| Config[Config]
  Validate -->|invalid| Error[Return startup error]
  Config --> Server[Server]
  Config --> Worker[Worker]
  Config --> Migrate[Migrate commands]
  Config --> Fixtures[Fixtures]
  Server --> Telemetry[Typed telemetry config]
  Worker --> Telemetry
Loading

Reviews (2): Last reviewed commit: "chore: merge main into branch, resolve g..." | Re-trigger Greptile

@shyim

shyim commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

Co-authored-by: shyim <6224096+shyim@users.noreply.github.com>

Copilot AI commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved. The only conflict was in api/go.mod — main had bumped the AWS SDK indirect deps while my branch added caarlos0/env. Took both sets of changes and ran go mod tidy to regenerate go.sum.

@shyim
shyim merged commit 85a0842 into main Aug 10, 2026
1 check passed
@shyim
shyim deleted the shyim/hearturchin branch August 10, 2026 05:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants