docs: refactor envc README and centralize decisions
README CLI: comment-first bash examples, convert/merge/get order, real-world flows. Move architecture decisions pointers into CONTRIBUTING; trim AGENTS and README. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,12 +17,9 @@ Breaking changes require a new major SemVer tag (or `/v2` module path if the pol
|
|||||||
## Testing policy, PR checklist, and releases
|
## Testing policy, PR checklist, and releases
|
||||||
|
|
||||||
Human-oriented detail lives in **[CONTRIBUTING.md](CONTRIBUTING.md)** (testing rules,
|
Human-oriented detail lives in **[CONTRIBUTING.md](CONTRIBUTING.md)** (testing rules,
|
||||||
`go test` / lint commands, Conventional Commits, and the release-please / GoReleaser flow).
|
`go test` / lint commands, Conventional Commits, the release-please / GoReleaser flow, and
|
||||||
Follow that document for any change that will ship in a versioned release.
|
**architecture decisions** / repo ASRs). Follow that document for any change that will ship
|
||||||
|
in a versioned release.
|
||||||
## Decisions
|
|
||||||
|
|
||||||
Architecture Significant Requirements: [docs/asr/README.md](docs/asr/README.md).
|
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -60,5 +60,9 @@ Local `go install` builds of `envc` report `dev` for `envc version` unless you p
|
|||||||
|
|
||||||
## Architecture decisions
|
## Architecture decisions
|
||||||
|
|
||||||
Repo-local ASRs: [docs/asr/README.md](docs/asr/README.md). Broader eSlider Go module
|
Significant design choices for this repository are captured as **Architecture Significant
|
||||||
conventions: `inventar/docs/asr/ASR-0008.md` (see [AGENTS.md](AGENTS.md) — Related).
|
Requirements** (ASRs):
|
||||||
|
|
||||||
|
- **Repo-local ASRs:** [docs/asr/README.md](docs/asr/README.md)
|
||||||
|
- **eSlider Go library conventions (inventar):** `inventar/docs/asr/ASR-0008.md` — also linked
|
||||||
|
from [AGENTS.md](AGENTS.md) under Related.
|
||||||
|
|||||||
@@ -87,100 +87,132 @@ os.WriteFile("out.json", b, 0o644)
|
|||||||
|
|
||||||
## CLI: `envc`
|
## CLI: `envc`
|
||||||
|
|
||||||
Formats for `--from` / `--to`: `yaml`, `json`, `ini`, `env`. Run `envc help` or
|
Install the binary from [**Install**](#install) (`go install …/cmd/envc@latest`). Every
|
||||||
`envc <command> -h` for flags.
|
subcommand uses **`--from`** and **`--to`** with one of **`yaml`**, **`json`**, **`ini`**,
|
||||||
|
**`env`**. Snippets use **`bash`** so you can copy-paste; replace paths and URLs with yours.
|
||||||
| Command | Purpose |
|
|
||||||
| ------------------------------------------------------------------ | --------------------------------------------------------------- |
|
|
||||||
| `envc convert --from yaml --to env --input - --output -` | Convert stdin YAML to dotenv on stdout |
|
|
||||||
| `envc get --from yaml --path service.name config.yaml` | Print one path (dot segments map to lower+alnum keys) |
|
|
||||||
| `envc merge --from yaml --to json --output out.json a.yaml b.yaml` | Deep-merge multiple YAML files, emit JSON |
|
|
||||||
|
|
||||||
### Help and version
|
### Help and version
|
||||||
|
|
||||||
```sh
|
```bash
|
||||||
|
# Root usage (commands, short descriptions)
|
||||||
envc help
|
envc help
|
||||||
|
|
||||||
|
# Per-command flag reference (convert, merge, get)
|
||||||
|
envc convert -h
|
||||||
|
envc merge -h
|
||||||
|
envc get -h
|
||||||
|
|
||||||
|
# Version string, git commit, build date (release builds embed the tag; local go install → dev)
|
||||||
envc version
|
envc version
|
||||||
```
|
```
|
||||||
|
|
||||||
### `convert` examples
|
### `convert`
|
||||||
|
|
||||||
```sh
|
One input → normalize keys → one output. Defaults: **`--input -`**, **`--output -`**
|
||||||
# Pipe YAML in, JSON on stdout (default --input - and --output -)
|
(stdin / stdout).
|
||||||
printf 'app:\n port: 8080\n' | envc convert --from yaml --to json
|
|
||||||
|
|
||||||
# File → file
|
```bash
|
||||||
envc convert --from json --to yaml --input settings.json --output settings.yaml
|
# Helm-style values file → JSON on the terminal (redirect to a file if you prefer)
|
||||||
|
envc convert --from yaml --to json --input ./values.yaml --output -
|
||||||
|
|
||||||
# Remote YAML → local dotenv
|
# Teammate’s JSON app settings → YAML for a repo that only accepts YAML
|
||||||
|
envc convert --from json --to yaml --input ./settings.json --output ./settings.yaml
|
||||||
|
|
||||||
|
# Windows-style INI → JSON for a one-off jq filter
|
||||||
|
envc convert --from ini --to json --input ./odbc.ini --output ./odbc.json
|
||||||
|
|
||||||
|
# Remote YAML → materialize .env for `docker compose --env-file` or similar
|
||||||
envc convert --from yaml --to env \
|
envc convert --from yaml --to env \
|
||||||
--input https://example.com/config.yaml \
|
--input "https://raw.githubusercontent.com/org/stack/main/config.yaml" \
|
||||||
--output .env.generated
|
--output ./.env.generated
|
||||||
|
|
||||||
|
# Tiny inline document → JSON (stdin is the pipe; same idea as --input -)
|
||||||
|
printf 'service:\n name: api\n port: 8443\n' | envc convert --from yaml --to json
|
||||||
```
|
```
|
||||||
|
|
||||||
### Apply YAML or INI to the **current** bash session
|
### `merge`
|
||||||
|
|
||||||
`envc convert … --to env` prints **dotenv-style** lines (`KEY=value`). They are normal
|
Several inputs in order: **nested maps combine**, **scalar leaves last-write-wins**, slices
|
||||||
shell assignments, not `export` lines, so use **`set -a`** (allexport) if child processes
|
default to **replace**. Optional **`--output -`** (stdout).
|
||||||
must see the variables. **Process substitution** `<(…)` needs **bash** (not plain `sh`).
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# YAML → current shell (and export to children while sourcing)
|
# Docker Compose: base + override → single JSON for another tool in the pipeline
|
||||||
set -a
|
envc merge --from yaml --to json \
|
||||||
source <(envc convert --from yaml --to env --input config.yaml)
|
./docker-compose.base.yaml \
|
||||||
set +a
|
./docker-compose.override.yaml
|
||||||
|
|
||||||
# INI → current shell
|
# App config: shipped defaults, local overrides, generated secrets → one merged YAML artifact
|
||||||
set -a
|
envc merge --from yaml --to yaml \
|
||||||
source <(envc convert --from ini --to env --input app.ini)
|
--output ./config.merged.yaml \
|
||||||
set +a
|
./config.defaults.yaml \
|
||||||
|
./config.local.yaml \
|
||||||
|
./config.secrets.yaml
|
||||||
|
|
||||||
|
# Hotfix on stdin, then merge with on-disk YAML (--from must match every input, including stdin)
|
||||||
|
cat ./patch-canary.yaml | envc merge --from yaml --to json - ./config.base.yaml ./config.prod.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Same idea from stdin:
|
### `get`
|
||||||
|
|
||||||
|
Print **one** scalar or JSON-encoded value. **`--path`** is dot-separated; each segment uses
|
||||||
|
the same **lower+alnum** rules as the library (`sub-service` / `SubService` → `subservice`).
|
||||||
|
Paths follow **maps only** (YAML lists are not walked by index here).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Image line from docker-compose (good for scripts: stdout is just the value)
|
||||||
|
envc get --from yaml --path services.api.image ./docker-compose.yaml
|
||||||
|
|
||||||
|
# Nested string from an application config on disk
|
||||||
|
envc get --from yaml --path database.url ./config/app.yaml
|
||||||
|
|
||||||
|
# Same lookup, but YAML arrives from curl (positional "-" = read stdin to EOF)
|
||||||
|
curl -fsSL https://config.example.com/app.yaml | envc get --from yaml --path database.url -
|
||||||
|
```
|
||||||
|
|
||||||
|
### Load YAML or INI into the current shell
|
||||||
|
|
||||||
|
**`--to env`** emits **`KEY=value`** (shell assignments, not `export`). Use **`set -a`**
|
||||||
|
(allexport) so child processes inherit variables while you **`source`**. **`source <(…)`**
|
||||||
|
requires **bash**. Only use with **trusted** input (same risk as any `source`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stack defaults from YAML into the current shell session
|
||||||
set -a
|
set -a
|
||||||
source <(cat deploy.yaml | envc convert --from yaml --to env)
|
source <(envc convert --from yaml --to env --input ./.env.defaults.yaml)
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# Legacy INI (e.g. PHP) → env-style assignments in the shell
|
||||||
|
set -a
|
||||||
|
source <(envc convert --from ini --to env --input ./legacy.ini)
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# Inline YAML here-doc → env → source (CI or local; no intermediate file)
|
||||||
|
set -a
|
||||||
|
source <(cat <<'YAML' | envc convert --from yaml --to env
|
||||||
|
app:
|
||||||
|
env: staging
|
||||||
|
region: eu-west-1
|
||||||
|
YAML
|
||||||
|
)
|
||||||
set +a
|
set +a
|
||||||
```
|
```
|
||||||
|
|
||||||
Only do this with **trusted** config files (same caution as `source` on any generated
|
### Stdin, URLs, and EOF
|
||||||
script): values are expanded by the shell when you `source` them.
|
|
||||||
|
|
||||||
### `get` examples
|
```bash
|
||||||
|
# Explicit stdin redirect (reads until EOF)
|
||||||
|
envc convert --from yaml --to json --input - --output - <./service.yaml
|
||||||
|
|
||||||
Path segments use the same **lower+alnum** rules as the library (e.g. `sub-service`
|
# Default is stdin/stdout — safe when stdin is a pipe or file; on an interactive TTY with no
|
||||||
and `SubService` both address `subservice`).
|
# pipe, the process waits for Ctrl-D, which looks like a "hang". Prefer --input path/URL in scripts.
|
||||||
|
printf 'k: v\n' | envc convert --from yaml --to json
|
||||||
|
|
||||||
```sh
|
# HTTPS GET with client timeout; full body is read into memory before convert
|
||||||
# Value from a file
|
envc convert --from json --to yaml \
|
||||||
envc get --from yaml --path app.port config.yaml
|
--input "https://api.example.com/v1/config.json" \
|
||||||
|
--output ./snapshot.yaml
|
||||||
# Nested key; stdin when the last argument is `-` or omitted with a pipe
|
|
||||||
cat config.yaml | envc get --from yaml --path service.subservice.name -
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `merge` examples
|
|
||||||
|
|
||||||
Sources are merged **in order** (later files override scalar leaves; maps recurse).
|
|
||||||
Use `-` once to read **one** merged stdin blob as a source (same format as the others).
|
|
||||||
|
|
||||||
```sh
|
|
||||||
envc merge --from yaml --to json defaults.yaml overrides.yaml
|
|
||||||
|
|
||||||
# Write merged JSON to stdout, then save
|
|
||||||
envc merge --from yaml --to json base.yaml local.yaml | tee merged.json
|
|
||||||
|
|
||||||
# Stdin plus files: first load stdin as YAML, then merge each file
|
|
||||||
cat patch.yaml | envc merge --from yaml --to yaml - base.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stdin and `-`
|
|
||||||
|
|
||||||
With `--input -` (convert) or `-` as the get/merge input, `envc` reads **until EOF**.
|
|
||||||
From an interactive terminal with no pipe, that waits until you press **Ctrl-D**
|
|
||||||
(end of input). Prefer `--input path` or a URL when scripting.
|
|
||||||
|
|
||||||
## API (all codecs)
|
## API (all codecs)
|
||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
@@ -212,13 +244,9 @@ INI uses dotted sections, e.g. `[service.subservice]` with `name=...`.
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Testing expectations, local commands, commit message conventions, and how **release-please**
|
Testing expectations, local commands, commit message conventions, how **release-please**
|
||||||
and **GoReleaser** publish tags and `envc` binaries are documented in
|
and **GoReleaser** publish tags and `envc` binaries, and **architecture decisions** (repo
|
||||||
**[CONTRIBUTING.md](CONTRIBUTING.md)**.
|
ASRs) are documented in **[CONTRIBUTING.md](CONTRIBUTING.md)**.
|
||||||
|
|
||||||
## Decisions
|
|
||||||
|
|
||||||
Repo-local ASRs: [docs/asr/README.md](docs/asr/README.md).
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user