Persist rewrote overrides.yaml with only this process's runtime values, so saving one key (for example websockets:generate-vapid-keys --update) dropped every key persisted earlier. It now starts from the saved file and lets runtime values win.
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; party merges plugin defaults into it during activation.
Features
- A fixed layer order, lowest to highest precedence:
- Plugin defaults added with
compass.Config.MergePlugin: a plugin'sconfig.yamlbecomes<plugin id>.<key>, any other<name>.yamlbecomes<plugin id>.<name>.<key>. <dir>/*.yaml(and*.yml): each file is a section named after the file, soconfig/app.yamlprovidesapp.*. Files load in sorted order.<dir>/env/<environment>/*.yaml: per-environment sections with the same naming.SUMMER_environment variables, including values from a.envfile (see Configuration).<dir>/env/<environment>/overrides.yaml, the file written bycompass.Config.Persist.- In-memory values stored with
compass.Config.Set.
- Plugin defaults added with
- Typed getters with zero-value defaults:
compass.Config.String,compass.Config.Int,compass.Config.Bool, pluscompass.Config.Lookupandcompass.Config.Hasto tell a missing key from a zero value. compass.Config.LoadSectionunmarshals a whole subtree into a struct usingkoanfstruct tags.- Runtime overrides:
compass.Config.Setchanges a value in memory,compass.Config.Persistsaves all runtime values atomically to the environment'soverrides.yaml, keeping the keys already saved there and replacing the saved value of any key set at runtime (directory mode 0700, file mode 0600, refusing any path outside the config directory), andcompass.Config.Reloadrereads every source and discards unsaved runtime values. compass.Config.Environmentreports the active environment name, which must consist of letters, digits,-and_.- Safe for concurrent reads and writes.
Usage
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:
# 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/v2with itsproviders/file,providers/env/v2,providers/confmapandparsers/yamlpackages. - Standard library:
fmt,io/fs,os,path/filepath,sort,strings,sync,unicode.
Testing
go test ./modules/compass/...
The tests use temporary directories and explicit environment lists and need no external services.