feat(go-config): multi-format codecs, envc CLI, fixtures, ASRs

- Module github.com/eslider/go-config with env/yaml/json/ini packages
- internal: source, keymap, merge, structconv, bytesutil
- cmd/envc: convert, get, merge
- docs/asr ASR-0001..0008, README/CHANGELOG/AGENTS refresh
- golangci-lint v2 config; tests + fixtures

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-02 14:04:01 +01:00
co-authored by Cursor
parent 288a5b600c
commit c575341178
75 changed files with 2810 additions and 528 deletions
@@ -0,0 +1,27 @@
# ASR-0001: Module rename and multi-package layout
## Context
The original `github.com/eslider/go-env` module only decoded process environment variables. The product scope expanded to YAML, JSON, INI, URLs, multi-source merging, and a CLI. The old name and single-package layout no longer matched the capability surface.
## Decision
1. The canonical module path is **`github.com/eslider/go-config`**.
2. Format-specific code lives in first-class subpackages: **`env/`**, **`yaml/`**, **`json/`**, **`ini/`**.
3. Shared building blocks live under **`internal/{source,keymap,merge,structconv,bytesutil}`**.
4. The CLI lives at **`cmd/envc/`** (one binary per inventar **ASR-0008** §2 — CLI inside the same module).
5. The GitHub repository is named **`eSlider/go-config`** (renamed from `go-env`).
## Consequences
- Consumers must change their `import` paths and `go get` target.
- CI, badges, and documentation reference `go-config` and `eslider/go-config`.
## Status
Accepted — 2026-05-02.
## References
- inventar ASR-0008 — Go library module conventions
- ASR-0002 — API clean break
@@ -0,0 +1,24 @@
# ASR-0002: Clean break from go-env v0.1.0
## Context
`go-env` v0.1.0 exposed package-level helpers (`Unmarshal`, `UnmarshalPrefix`, `AsMap`, …). The new design is `Codec`-centric with multiple sources per format.
## Decision
1. **No compatibility shims** in `go-config` re-exporting the old API.
2. v0.1.0 remains tagged on git history as `github.com/eslider/go-env@v0.1.0` for archival consumers.
3. Migration path is documented in `CHANGELOG.md` and the README.
## Consequences
- Single-call-site migrations must switch to `env.New(env.WithCurrentEnvironment(), …).Unmarshal(&cfg)`.
## Status
Accepted — 2026-05-02.
## References
- CHANGELOG `[0.2.0]`
- ASR-0001
@@ -0,0 +1,25 @@
# ASR-0003: Uniform codec API and Source abstraction
## Context
Users switch between YAML, JSON, INI, and env-file formats; a different function surface per format would increase cognitive load and test matrices.
## Decision
1. Every format package exports **`New(opts...) *Codec`** with **`Map`**, **`Unmarshal`**, **`UnmarshalContext`**, **`Marshal`**, **`WriteTo`**.
2. Bytes, readers, files, and URLs are modeled by **`internal/source.Source`** with **`Open(ctx) (io.ReadCloser, error)`**.
3. HTTP(S) sources support **`WithHTTPHeader`** and **`WithHTTPClient`**.
## Consequences
- Cross-format examples in the README stay structurally identical.
- URL behaviour is testable with `httptest` against our client only.
## Status
Accepted — 2026-05-02.
## References
- ASR-0005 — merge semantics across multiple sources
- ASR-0008 — httptest policy alignment
@@ -0,0 +1,23 @@
# ASR-0004: Configurable key normalization
## Context
Real-world configuration mixes `kebab-case`, `snake_case`, `SCREAMING_SNAKE`, and struct field names. Requiring `mapstructure` tags on every field is noisy.
## Decision
1. After parsing and merging, codecs run **`internal/keymap.Walk`** with a **`Normalizer`** (default **`LowerAlnum`**: lowercase + strip non `[a-z0-9]`).
2. Callers may set **`WithKeyNormalizer(nil)`** to disable walking, or supply a custom function.
## Consequences
- POSIX env keys without `-` still align with YAML keys that contain hyphens once normalized.
- CLI `get` applies the same normalizer to each path segment for consistency.
## Status
Accepted — 2026-05-02.
## References
- ASR-0003
@@ -0,0 +1,26 @@
# ASR-0005: Multi-source deep-merge semantics
## Context
Layered configuration (defaults file < local overrides < process environment) requires predictable composition rules.
## Decision
1. Sources are applied in **option order** (left → right). Each source parses to a `map[string]any`, then **`merge.DeepMerge`** folds them.
2. **Maps recurse** — nested keys are unioned; existing nested maps are not replaced wholesale by a sibling scalar.
3. **Scalar leaves** use **last-write-wins** (later source wins).
4. **Slices** default to **`merge.Replace`**; **`WithSliceMerge(merge.Concat)`** appends prior and new slice elements.
## Consequences
- Env files and `WithCurrentEnvironment()` compose when ordered **lowest → highest** priority.
- Tests lock behaviour via `fixtures/merge/*`.
## Status
Accepted — 2026-05-02.
## References
- ASR-0003
- `internal/merge`
@@ -0,0 +1,26 @@
# ASR-0006: Third-party library choices
## Context
We need battle-tested parsers and struct bridging without maintaining our own grammars.
## Decision
1. **Struct / map bridging:** `github.com/go-viper/mapstructure/v2` (maintained fork of `mitchellh/mapstructure`).
2. **YAML:** `gopkg.in/yaml.v3`.
3. **JSON:** `encoding/json` (stdlib).
4. **INI:** `gopkg.in/ini.v1` with `Loose` parsing for tolerant files.
5. **`.env` files:** `github.com/joho/godotenv` (`UnmarshalBytes`).
6. **CLI:** stdlib `flag` only (no Cobra).
## Consequences
- `go.mod` carries the above direct dependencies (plus transitive modules as resolved by MVS).
## Status
Accepted — 2026-05-02.
## References
- [go-viper/mapstructure](https://github.com/go-viper/mapstructure)
@@ -0,0 +1,24 @@
# ASR-0007: CLI `envc` — convert, get, merge
## Context
Operators need a small tool to translate configuration between formats and to inspect merged trees without writing Go.
## Decision
1. Binary name **`envc`**; module path **`github.com/eslider/go-config/cmd/envc`** (`go install …/cmd/envc@latest`).
2. Subcommands: **`convert`**, **`get`**, **`merge`** — each implemented as a **`Run*(args, stdin, stdout, stderr) int`** function for in-process tests.
3. **No** third-party CLI framework in v1 of the CLI.
## Consequences
- `cmd/envc` may grow flags; keep business logic in subpackages, not in `main` beyond wiring.
## Status
Accepted — 2026-05-02.
## References
- inventar ASR-0008 §2 — CLI placement
- ASR-0003
@@ -0,0 +1,25 @@
# ASR-0008: GitHub presentation and release flow
## Context
The repository was renamed from `eSlider/go-env` to `eSlider/go-config`. The v0.1.0 tag existed locally before the rename. Presentation should match other eSlider `go-*` libraries (e.g. `go-matrix-bot`).
## Decision
1. **v0.1.0 preservation:** push tag `v0.1.0` and create a GitHub **Release** with notes extracted from `CHANGELOG.md` before landing breaking work.
2. **Development branch:** `release/v2` carries the `go-config` implementation until merged to `main`.
3. **Rename:** `gh repo rename go-config` from `go-env`; local `origin` URL updated to `https://github.com/eSlider/go-config.git`.
4. **Metadata:** `gh repo edit` sets description, **`homepage`** `https://pkg.go.dev/github.com/eslider/go-config`, and **topics** (`go`, `golang`, `config`, `yaml`, `json`, `ini`, `env`, `dotenv`, `mapstructure`, `codec`, `encoder`, `decoder`, `cli`, `library`).
5. **README:** badges row, mermaid architecture diagram, hero snippet, quick starts, CLI table, API tables, related libraries, link to `docs/asr/`.
## Consequences
- Old `github.com/eslider/go-env` URLs redirect for a limited GitHub window; module path for v0.1.0 code remains the historical `go-env` import.
## Status
Accepted — 2026-05-02.
## References
- [go-matrix-bot](https://github.com/eSlider/go-matrix-bot) — README pattern reference
+14
View File
@@ -0,0 +1,14 @@
# Architecture Significant Requirements (ASRs)
Repo-local decisions for `go-config` (numbering starts at ASR-0001). Org-wide policies remain under `inventar/docs/asr/`.
| ID | Title |
| --- | --- |
| [ASR-0001](ASR-0001-module-rename-and-layout.md) | Module rename and multi-package layout |
| [ASR-0002](ASR-0002-clean-break-from-v0.1.0.md) | Clean break from go-env v0.1.0 |
| [ASR-0003](ASR-0003-uniform-codec-api-and-source-abstraction.md) | Uniform codec API and Source abstraction |
| [ASR-0004](ASR-0004-configurable-key-normalization.md) | Configurable key normalization |
| [ASR-0005](ASR-0005-multi-source-deep-merge-semantics.md) | Multi-source deep-merge semantics |
| [ASR-0006](ASR-0006-third-party-library-choices.md) | Third-party library choices |
| [ASR-0007](ASR-0007-cli-envc-convert-get-merge.md) | CLI `envc`: convert, get, merge |
| [ASR-0008](ASR-0008-github-presentation-and-release-flow.md) | GitHub presentation and release flow |