v0.2.0 toml-only, harden concurrency and env export, dep updates
Test / test (push) Canceled after 0s
Test / test (push) Canceled after 0s
This commit is contained in:
1 parent
3c35601179
commit
e08467bf38
42 files changed
+2533
-4274
No files matched your search
@@ -1,68 +1,113 @@
|
||||
# Config
|
||||
# config
|
||||
|
||||
Thread-safe configuration management for Go applications with support for multiple sources (files, environment variables, command-line arguments, defaults) and configurable precedence.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiple Sources**: Load configuration from defaults, files, environment variables, and CLI arguments
|
||||
- **Configurable Precedence**: Control which sources override others
|
||||
- **Type Safety**: Struct-based configuration with automatic validation
|
||||
- **Thread-Safe**: Concurrent access with read-write locking
|
||||
- **File Watching**: Automatic reloading on configuration changes
|
||||
- **Source Tracking**: Know exactly where each value came from
|
||||
- **Tag Support**: Use `toml`, `json`, or `yaml` struct tags
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get github.com/lixenwraith/config
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
Thread-safe configuration from TOML files, environment variables, command-line
|
||||
arguments and registered defaults. Requires Go 1.27.1. The only runtime dependency
|
||||
is [lixenwraith/toml](https://github.com/lixenwraith/toml), pinned to merged commit
|
||||
`b59d068ff6a4`. Testify and its dependencies are used only by tests.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
"github.com/lixenwraith/config"
|
||||
)
|
||||
|
||||
type AppConfig struct {
|
||||
Server struct {
|
||||
Host string `toml:"host"`
|
||||
Port int `toml:"port"`
|
||||
} `toml:"server"`
|
||||
Debug bool `toml:"debug"`
|
||||
type Settings struct {
|
||||
Port uint16 `toml:"port"`
|
||||
Timeout time.Duration `toml:"timeout"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
defaults := &AppConfig{}
|
||||
defaults.Server.Host = "localhost"
|
||||
defaults.Server.Port = 8080
|
||||
|
||||
cfg, err := config.Quick(defaults, "MYAPP_", "config.toml")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
port, _ := cfg.Get("server.port")
|
||||
log.Printf("Server port: %d", port.(int64))
|
||||
initial := Settings{Port: 8080, Timeout: 5 * time.Second}
|
||||
cfg, err := config.NewBuilder().
|
||||
WithTarget(&initial).
|
||||
WithFile("config.toml").
|
||||
WithEnvPrefix("APP_").
|
||||
WithArgs([]string{"--port=9090"}).
|
||||
Build()
|
||||
if err != nil { log.Fatal(err) }
|
||||
latest, err := cfg.AsStruct()
|
||||
if err != nil { log.Fatal(err) }
|
||||
log.Printf("%+v", latest.(*Settings))
|
||||
}
|
||||
```
|
||||
|
||||
```toml
|
||||
# Service settings
|
||||
timeout = "10s"
|
||||
port = 8080
|
||||
```
|
||||
|
||||
Precedence is CLI > environment > file > default. Register paths with `Register`
|
||||
or structs with `RegisterStruct`; defaults define the types used to validate new
|
||||
values. `LoadFile`, `LoadEnv` and `LoadCLI` replace their complete source. A failed
|
||||
source load keeps its previous values. Removing an environment variable, CLI flag
|
||||
or file key removes the old override on the next load.
|
||||
|
||||
`Get` returns a detached raw source value: environment and CLI values remain
|
||||
strings, TOML integers are int64. `GetTyped[T]`, `Scan`, `ScanSource`, `ScanTyped[T]`
|
||||
and `ScanMap` provide checked conversions. Errors leave destinations unchanged;
|
||||
missing struct fields retain their existing values. `AsStruct` returns a detached
|
||||
snapshot on every call. Build fills `WithTarget` once; subsequent reloads never
|
||||
mutate that caller-owned object.
|
||||
|
||||
## Migration
|
||||
|
||||
- Only TOML files and `toml` tags are supported. JSON/YAML parsers, format constants
|
||||
and alternate tag modes are removed. Existing format/tag option methods accept
|
||||
only `toml` (`auto` remains an alias for TOML for file options).
|
||||
- Numeric conversion rejects overflow, negative unsigned inputs, fractional
|
||||
integer conversion, non-finite floats and integer-to-float precision loss.
|
||||
Float64-to-float32 rounding within range remains supported. Full uint64 is
|
||||
supported in memory; saving a value above MaxInt64 fails without changing the
|
||||
destination file. Use an application-level string representation if needed.
|
||||
- Strings can represent decimal numbers, booleans, comma-separated slices/arrays,
|
||||
durations, RFC3339 timestamps, IP addresses, CIDR networks and URLs. Boolean to
|
||||
string conversion uses `true`/`false`. There are no user decode hooks or automatic
|
||||
`encoding.TextUnmarshaler` calls.
|
||||
- Setters and source loads reject values incompatible with registered defaults
|
||||
immediately. Check their errors. Untyped nil defaults leave the value type open;
|
||||
typed nil pointers retain their declared type. Unsupported values, non-finite
|
||||
floats and cyclic/deep graphs are rejected. Configuration value depth is 128.
|
||||
- All public values, defaults, options and clones own their mutable containers.
|
||||
Existing references returned from `AsStruct` are snapshots, not live objects.
|
||||
- Saves standardize formatting and retain loaded comments as a preamble. Comment
|
||||
positions are not preserved. Existing file permissions are retained; new files
|
||||
use 0600. Only registered paths are saved, so unknown file keys are omitted.
|
||||
- Paths use dot-separated ASCII key segments. Numeric-only segments and overlapping
|
||||
registrations such as `server` plus `server.port` are rejected. Embedded fields
|
||||
are not promoted. Recursive struct schemas are unsupported.
|
||||
|
||||
## Watching
|
||||
|
||||
```go
|
||||
changes := cfg.WatchWithOptions(config.DefaultWatchOptions())
|
||||
defer cfg.StopAutoUpdate()
|
||||
for event := range changes {
|
||||
// Events are hints; always read the latest complete state.
|
||||
log.Print(event)
|
||||
latest, err := cfg.AsStruct()
|
||||
if err != nil { log.Print(err); continue }
|
||||
_ = latest.(*Settings)
|
||||
}
|
||||
```
|
||||
|
||||
The watcher fingerprints file contents, waits for a quiet debounce interval, then
|
||||
validates and publishes. Invalid edits, timeouts, deletion and blocked permission
|
||||
changes retain the last valid state. Cancelled, expired or superseded reloads
|
||||
cannot publish. Stop closes subscriptions synchronously; a new Watch call can
|
||||
restart watching. Notifications are buffered and best effort.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Quick Start](doc/quick-start.md) - Get started
|
||||
- [Builder Pattern](doc/builder.md) - Advanced configuration with the builder
|
||||
- [Access Patterns](doc/access.md) - Getting and setting values
|
||||
- [Command Line](doc/cli.md) - CLI argument handling
|
||||
- [Environment Variables](doc/env.md) - Environment variable handling
|
||||
- [File Configuration](doc/file.md) - File formats and loading
|
||||
- [Validation](doc/validator.md) - Validation functions and integration
|
||||
- [Live Reconfiguration](doc/reconfiguration.md) - File watching and auto-update on change
|
||||
- [Quick Guide](doc/quick-guide_lixenwraith_config.md) - Quick reference guide
|
||||
- [Quick start](doc/quick-start.md), [builder](doc/builder.md), [access](doc/access.md)
|
||||
- [Files and saving](doc/file.md), [environment](doc/env.md), [CLI](doc/cli.md)
|
||||
- [Live reconfiguration](doc/reconfiguration.md), [validation](doc/validator.md)
|
||||
- [Architecture](doc/architecture.md), [audit evidence](doc/audit.md)
|
||||
|
||||
## License
|
||||
Run `go test -race ./...` and `go vet ./...`. CI also executes the 386 suite,
|
||||
cross-compiles for Windows and runs bounded numeric conversion fuzzing.
|
||||
|
||||
BSD-3-Clause
|
||||
BSD-3-Clause; see LICENSE.
|
||||
Reference in new issue
Block a user