113 lines
5.5 KiB
Markdown
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.
|