docs: add CONTRIBUTING guide and expand README
Add CONTRIBUTING.md for testing, PR checks, and release-please flow; link from README and AGENTS. Document envc bash sourcing (set -a, source <(...)) for YAML/INI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,39 +14,16 @@ Convert **env**, **YAML**, **JSON**, and **INI** into nested `map[string]any` an
|
|||||||
|
|
||||||
Breaking changes require a new major SemVer tag (or `/v2` module path if the policy changes).
|
Breaking changes require a new major SemVer tag (or `/v2` module path if the policy changes).
|
||||||
|
|
||||||
## Testing policy
|
## Testing policy, PR checklist, and releases
|
||||||
|
|
||||||
Follows the eSlider "no synthetic mocks" policy (see [.cursor/rules/no-synthetic-mocks.mdc](.cursor/rules/no-synthetic-mocks.mdc)):
|
Human-oriented detail lives in **[CONTRIBUTING.md](CONTRIBUTING.md)** (testing rules,
|
||||||
|
`go test` / lint commands, Conventional Commits, and the release-please / GoReleaser flow).
|
||||||
- **Unit tests** — pure inputs for merge, keymap, structconv, env flat-map helpers.
|
Follow that document for any change that will ship in a versioned release.
|
||||||
- **`httptest`** — allowed only to test **our** HTTP client behaviour in `internal/source` (not third-party API emulation).
|
|
||||||
- **Fixtures** — real files under `fixtures/`; `testfixtures` resolves paths from module root.
|
|
||||||
|
|
||||||
## Decisions
|
## Decisions
|
||||||
|
|
||||||
Architecture Significant Requirements: [docs/asr/README.md](docs/asr/README.md).
|
Architecture Significant Requirements: [docs/asr/README.md](docs/asr/README.md).
|
||||||
|
|
||||||
## Checklist before release
|
|
||||||
|
|
||||||
Local sanity check (CI runs the same on every PR):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
go mod tidy
|
|
||||||
go vet ./...
|
|
||||||
go test -race -shuffle=on -count=1 ./...
|
|
||||||
golangci-lint run --timeout 5m
|
|
||||||
```
|
|
||||||
|
|
||||||
Versioning is **automated via [release-please](https://github.com/googleapis/release-please-action)**
|
|
||||||
and [GoReleaser](https://goreleaser.com) — do **not** hand-edit version strings or tag manually:
|
|
||||||
|
|
||||||
1. Commit using [Conventional Commits](https://www.conventionalcommits.org/)
|
|
||||||
(`feat:`, `fix:`, `feat!:` for breaking, etc.).
|
|
||||||
2. `.github/workflows/release-please.yml` opens a release PR on each push to `main`
|
|
||||||
with the computed next SemVer and an updated `CHANGELOG.md`.
|
|
||||||
3. Merging that PR creates the `vX.Y.Z` tag. `.github/workflows/release.yml` then runs
|
|
||||||
GoReleaser to publish cross-platform `envc` binaries and a GitHub Release.
|
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
- `inventar/docs/asr/ASR-0008.md` — Go library module conventions
|
- `inventar/docs/asr/ASR-0008.md` — Go library module conventions
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Contributing to go-config
|
||||||
|
|
||||||
|
Thank you for helping improve this module. This document covers **testing expectations**,
|
||||||
|
**local checks**, and **how releases are cut** (so nobody tags or edits versions by hand).
|
||||||
|
|
||||||
|
For repository purpose, public API notes, and agent-oriented context, see [AGENTS.md](AGENTS.md).
|
||||||
|
|
||||||
|
## Testing policy
|
||||||
|
|
||||||
|
This project follows the eSlider **no synthetic mocks** policy (see [.cursor/rules/no-synthetic-mocks.mdc](.cursor/rules/no-synthetic-mocks.mdc)):
|
||||||
|
|
||||||
|
- **Unit tests** — pure inputs for merge, keymap, structconv, env flat-map helpers.
|
||||||
|
- **`httptest`** — allowed only to test **our** HTTP client behaviour in `internal/source` (not third-party API emulation).
|
||||||
|
- **Fixtures** — real files under `fixtures/`; `testfixtures` resolves paths from module root.
|
||||||
|
|
||||||
|
## Before you open a pull request
|
||||||
|
|
||||||
|
Run the same checks CI will run (adjust paths if your checkout layout differs):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go mod tidy
|
||||||
|
go vet ./...
|
||||||
|
go test -race -shuffle=on -count=1 ./...
|
||||||
|
golangci-lint run --timeout 5m
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commit messages
|
||||||
|
|
||||||
|
Use [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `perf:`,
|
||||||
|
`feat!:` or `BREAKING CHANGE:` for breaking changes, etc.). Release automation reads these
|
||||||
|
messages to choose the next SemVer.
|
||||||
|
|
||||||
|
## Release and versioning
|
||||||
|
|
||||||
|
Versions are **computed from git history**, not edited in Go source. Do **not** create
|
||||||
|
release tags manually or bump version constants in code.
|
||||||
|
|
||||||
|
### CI on every change
|
||||||
|
|
||||||
|
1. Every push and pull request runs [`test.yml`](.github/workflows/test.yml) (matrix: Go
|
||||||
|
1.22, 1.23, stable × linux / macOS / windows, with `-race -shuffle=on`) and
|
||||||
|
[`lint.yml`](.github/workflows/lint.yml) (`go vet` + `golangci-lint`).
|
||||||
|
|
||||||
|
### Release Please and GoReleaser
|
||||||
|
|
||||||
|
2. On pushes to `main`, [`release-please.yml`](.github/workflows/release-please.yml) parses
|
||||||
|
commits since the last tag and opens (or updates) a **release pull request** that bumps
|
||||||
|
[`CHANGELOG.md`](CHANGELOG.md) and [`.release-please-manifest.json`](.release-please-manifest.json)
|
||||||
|
to the next SemVer.
|
||||||
|
|
||||||
|
3. **Merging that release PR** creates the git tag `vX.Y.Z` and a GitHub Release.
|
||||||
|
|
||||||
|
4. The tag triggers [`release.yml`](.github/workflows/release.yml), which runs
|
||||||
|
[GoReleaser](https://goreleaser.com) to cross-compile the `envc` binary (linux / darwin /
|
||||||
|
windows × amd64 / arm64), injects `-X main.version={{.Version}}` (and related ldflags) at
|
||||||
|
link time, and attaches archives plus `checksums.txt` to the release.
|
||||||
|
|
||||||
|
Local `go install` builds of `envc` report `dev` for `envc version` unless you pass your own
|
||||||
|
`-ldflags`; release binaries show the released version.
|
||||||
|
|
||||||
|
## Architecture decisions
|
||||||
|
|
||||||
|
Repo-local ASRs: [docs/asr/README.md](docs/asr/README.md). Broader eSlider Go module
|
||||||
|
conventions: `inventar/docs/asr/ASR-0008.md` (see [AGENTS.md](AGENTS.md) — Related).
|
||||||
@@ -87,12 +87,100 @@ os.WriteFile("out.json", b, 0o644)
|
|||||||
|
|
||||||
## CLI: `envc`
|
## CLI: `envc`
|
||||||
|
|
||||||
|
Formats for `--from` / `--to`: `yaml`, `json`, `ini`, `env`. Run `envc help` or
|
||||||
|
`envc <command> -h` for flags.
|
||||||
|
|
||||||
| Command | Purpose |
|
| Command | Purpose |
|
||||||
| ------------------------------------------------------------------ | --------------------------------------------------------------- |
|
| ------------------------------------------------------------------ | --------------------------------------------------------------- |
|
||||||
| `envc convert --from yaml --to env --input - --output -` | Convert stdin YAML to dotenv on stdout |
|
| `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-separated; segments normalized like codecs) |
|
| `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 |
|
| `envc merge --from yaml --to json --output out.json a.yaml b.yaml` | Deep-merge multiple YAML files, emit JSON |
|
||||||
|
|
||||||
|
### Help and version
|
||||||
|
|
||||||
|
```sh
|
||||||
|
envc help
|
||||||
|
envc version
|
||||||
|
```
|
||||||
|
|
||||||
|
### `convert` examples
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Pipe YAML in, JSON on stdout (default --input - and --output -)
|
||||||
|
printf 'app:\n port: 8080\n' | envc convert --from yaml --to json
|
||||||
|
|
||||||
|
# File → file
|
||||||
|
envc convert --from json --to yaml --input settings.json --output settings.yaml
|
||||||
|
|
||||||
|
# Remote YAML → local dotenv
|
||||||
|
envc convert --from yaml --to env \
|
||||||
|
--input https://example.com/config.yaml \
|
||||||
|
--output .env.generated
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apply YAML or INI to the **current** bash session
|
||||||
|
|
||||||
|
`envc convert … --to env` prints **dotenv-style** lines (`KEY=value`). They are normal
|
||||||
|
shell assignments, not `export` lines, so use **`set -a`** (allexport) if child processes
|
||||||
|
must see the variables. **Process substitution** `<(…)` needs **bash** (not plain `sh`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# YAML → current shell (and export to children while sourcing)
|
||||||
|
set -a
|
||||||
|
source <(envc convert --from yaml --to env --input config.yaml)
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# INI → current shell
|
||||||
|
set -a
|
||||||
|
source <(envc convert --from ini --to env --input app.ini)
|
||||||
|
set +a
|
||||||
|
```
|
||||||
|
|
||||||
|
Same idea from stdin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
set -a
|
||||||
|
source <(cat deploy.yaml | envc convert --from yaml --to env)
|
||||||
|
set +a
|
||||||
|
```
|
||||||
|
|
||||||
|
Only do this with **trusted** config files (same caution as `source` on any generated
|
||||||
|
script): values are expanded by the shell when you `source` them.
|
||||||
|
|
||||||
|
### `get` examples
|
||||||
|
|
||||||
|
Path segments use the same **lower+alnum** rules as the library (e.g. `sub-service`
|
||||||
|
and `SubService` both address `subservice`).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Value from a file
|
||||||
|
envc get --from yaml --path app.port config.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 |
|
||||||
@@ -122,26 +210,11 @@ INI uses dotted sections, e.g. `[service.subservice]` with `name=...`.
|
|||||||
| [go-onlyoffice](https://github.com/eSlider/go-onlyoffice) | OnlyOffice API |
|
| [go-onlyoffice](https://github.com/eSlider/go-onlyoffice) | OnlyOffice API |
|
||||||
| [go-ollama](https://github.com/eSlider/go-ollama) | Ollama client |
|
| [go-ollama](https://github.com/eSlider/go-ollama) | Ollama client |
|
||||||
|
|
||||||
## Release flow
|
## Contributing
|
||||||
|
|
||||||
Versions are **computed from git history**, never hardcoded in source. Commit with
|
Testing expectations, local commands, commit message conventions, and how **release-please**
|
||||||
[Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `feat!:` …)
|
and **GoReleaser** publish tags and `envc` binaries are documented in
|
||||||
and the pipeline takes care of the rest:
|
**[CONTRIBUTING.md](CONTRIBUTING.md)**.
|
||||||
|
|
||||||
1. Every push / PR runs [`test.yml`](.github/workflows/test.yml) (matrix: Go 1.22, 1.23,
|
|
||||||
stable × linux/macOS/windows, `-race -shuffle=on`) and
|
|
||||||
[`lint.yml`](.github/workflows/lint.yml) (`go vet` + `golangci-lint`).
|
|
||||||
2. On merges to `main`, [`release-please.yml`](.github/workflows/release-please.yml)
|
|
||||||
parses commits since the last tag and opens a "release PR" that bumps `CHANGELOG.md`
|
|
||||||
and `.release-please-manifest.json` to the next SemVer.
|
|
||||||
3. Merging that PR creates the git tag `vX.Y.Z` and a GitHub Release.
|
|
||||||
4. The tag triggers [`release.yml`](.github/workflows/release.yml), which runs
|
|
||||||
[GoReleaser](https://goreleaser.com) to cross-compile `envc` (linux/darwin/windows ×
|
|
||||||
amd64/arm64) with `-X main.version={{.Version}}` injected at link time and attach
|
|
||||||
archives + `checksums.txt` to the release.
|
|
||||||
|
|
||||||
No version string lives in Go source — `envc version` prints the value baked in by the
|
|
||||||
release build, or `dev` for local `go install` builds.
|
|
||||||
|
|
||||||
## Decisions
|
## Decisions
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user