# 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 `.`, any other `.yaml` becomes `..`. 2. `/*.yaml` (and `*.yml`): each file is a section named after the file, so `config/app.yaml` provides `app.*`. Files load in sorted order. 3. `/env//*.yaml`: per-environment sections with the same naming. 4. `SUMMER_` environment variables, including values from a `.env` file (see Configuration). 5. `/env//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//`. An explicit `compass.Options.Env` wins over it. | | `SUMMER_
__` | 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.