Files
summercms/modules/compass/README.md

113 lines
5.5 KiB
Markdown

# compass
Layered YAML configuration with per-environment directories, `SUMMER_` environment overrides, embedded plugin defaults and dot-path access.
`import "git.golem15.com/golem15/summercms/modules/compass"`
## Overview
`compass` is the SummerCMS counterpart of WinterCMS's `config/*.php` files, per-environment config directories, `.env` support and `Config::get('app.name')`. It merges several layers into one tree built on koanf, and every key is read with a dot path such as `app.name`. Plugin defaults live under the bare plugin ID (`acme.blog.posts_per_page`) instead of WinterCMS's `acme.blog::posts_per_page`. The generated `main` of an application calls `compass.Load("config")` and hands the result to [backpack](../backpack/README.md); [party](../party/README.md) merges plugin defaults into it during activation.
## Features
- A fixed layer order, lowest to highest precedence:
1. Plugin defaults added with `compass.Config.MergePlugin`: a plugin's `config.yaml` becomes `<plugin id>.<key>`, any other `<name>.yaml` becomes `<plugin id>.<name>.<key>`.
2. `<dir>/*.yaml` (and `*.yml`): each file is a section named after the file, so `config/app.yaml` provides `app.*`. Files load in sorted order.
3. `<dir>/env/<environment>/*.yaml`: per-environment sections with the same naming.
4. `SUMMER_` environment variables, including values from a `.env` file (see Configuration).
5. `<dir>/env/<environment>/overrides.yaml`, the file written by `compass.Config.Persist`.
6. In-memory values stored with `compass.Config.Set`.
- Typed getters with zero-value defaults: `compass.Config.String`, `compass.Config.Int`, `compass.Config.Bool`, plus `compass.Config.Lookup` and `compass.Config.Has` to tell a missing key from a zero value.
- `compass.Config.LoadSection` unmarshals a whole subtree into a struct using `koanf` struct tags.
- Runtime overrides: `compass.Config.Set` changes a value in memory, `compass.Config.Persist` saves all runtime values atomically to the environment's `overrides.yaml` (directory mode 0700, file mode 0600, refusing any path outside the config directory), and `compass.Config.Reload` rereads every source and discards unsaved runtime values.
- `compass.Config.Environment` reports the active environment name, which must consist of letters, digits, `-` and `_`.
- Safe for concurrent reads and writes.
## Usage
```go
package blog
import (
"git.golem15.com/golem15/summercms/modules/compass"
)
type mailSettings struct {
Host string `koanf:"host"`
Port int `koanf:"port"`
}
func loadConfig() (*compass.Config, error) {
cfg, err := compass.Open(compass.Options{Dir: "config", Env: "development"})
if err != nil {
return nil, err
}
name := cfg.String("app.name")
perPage := cfg.Int("acme.blog.posts_per_page")
_, _ = name, perPage
var mail mailSettings
if err := cfg.LoadSection("mail", &mail); err != nil {
return nil, err
}
// Store a runtime override and write it to config/env/development/overrides.yaml.
if err := cfg.Set("acme.blog.posts_per_page", 25); err != nil {
return nil, err
}
return cfg, cfg.Persist()
}
```
A matching configuration directory:
```yaml
# config/app.yaml
name: Acme
url: http://localhost:8080
# config/env/development/app.yaml
debug: true
```
## API reference
| Identifier | Description |
|------------|-------------|
| `compass.Config` | The merged configuration tree with dot-path access. |
| `compass.Load` | Opens a config directory, taking the environment from `SUMMER_ENV`. |
| `compass.Open` | Opens configuration with explicit `compass.Options`. |
| `compass.Options` | Config directory, environment name and the environment variable list to read (defaults to the process environment). |
| `compass.Config.String` / `compass.Config.Int` / `compass.Config.Bool` | Typed getters that return the zero value for a missing key. |
| `compass.Config.Lookup` / `compass.Config.Has` | Raw value lookup and existence check. |
| `compass.Config.LoadSection` | Unmarshals a subtree into a struct with `koanf` tags. |
| `compass.Config.MergePlugin` | Adds a plugin's embedded default configuration under its plugin ID. |
| `compass.Config.Set` / `compass.Config.Persist` / `compass.Config.Reload` | Runtime overrides, saving them to disk, and rebuilding from disk. |
| `compass.Config.Environment` | Returns the active environment name. |
## Configuration
`compass` reads the process environment (or `compass.Options.Environ` when it is set):
| Variable | Default | Effect |
|----------|---------|--------|
| `SUMMER_ENV` | `production` | Selects the environment directory `config/env/<name>/`. An explicit `compass.Options.Env` wins over it. |
| `SUMMER_<SECTION>__<KEY>` | none | Overrides a config key: the prefix is removed, `__` separates path segments and the name is lower-cased, so `SUMMER_DATABASE__DSN` sets `database.dsn` and `SUMMER_ADMIN__JWT__SECRET` sets `admin.jwt.secret`. |
A `.env` file in the parent directory of the config directory (next to `config/`) supplies `KEY=VALUE` lines, with optional `export` prefixes and quotes, for variables that are not already set in the real environment. It never modifies the process environment.
## Dependencies
- SummerCMS modules: none.
- Third-party: `github.com/knadh/koanf/v2` with its `providers/file`, `providers/env/v2`, `providers/confmap` and `parsers/yaml` packages.
- Standard library: `fmt`, `io/fs`, `os`, `path/filepath`, `sort`, `strings`, `sync`, `unicode`.
## Testing
```sh
go test ./modules/compass/...
```
The tests use temporary directories and explicit environment lists and need no external services.