Use a go-billy filesystem¶
go-billy is the filesystem abstraction go-git
is built on, so a tool that already holds a billy.Filesystem — an in-memory worktree, a chroot, an
on-disk root — can point config at it through the
config-billy sibling module.
import (
"github.com/go-git/go-billy/v5/osfs"
"gitlab.com/phpboyscout/go/config"
configbilly "gitlab.com/phpboyscout/go/config-billy"
)
bfs := osfs.New("/etc/app") // or an in-memory memfs, a chroot, a worktree
store, err := config.NewStore(ctx,
config.WithFiles(configbilly.Wrap(bfs), "config.yaml"),
config.WithEnv("APP"),
)
Wrap is the whole API, and the layer reads and writes — a store.Apply commits back into the
billy filesystem through its native rename, so an edit lands atomically and structure is preserved.
Paths are forward-slash and relative to the filesystem's root, like config.Dir and unlike
config.OS(): an osfs.New("/etc/app") reads "config.yaml", not "/etc/app/config.yaml".
Read-only billy filesystems¶
A billy.Filesystem can be mounted without write capability. When it is, config-billy maps that to
the shared sentinel config.ErrReadOnlyFS up front, so a read-only filesystem gives a consistent
errors.Is story whether it is read-only by design or by how it was mounted:
_, err := store.Apply(ctx, config.Set("server.port", 9090))
if errors.Is(err, config.ErrReadOnlyFS) {
// the wrapped billy filesystem reports no WriteCapability
}
Symlinks¶
billy.Filesystem mandates Readlink, so config-billy satisfies config.LinkReader for every
wrapped filesystem — a config file reached through a symlink is written through it, rather than
having the link replaced with a regular file. Where a path is not a link, the filesystem says so and
the path is used as given.
What it costs¶
go-billy, config, and what those two already bring — asserted by an allowlist test in the module,
so an unforeseen transitive addition fails its build rather than arriving quietly.
Related¶
- How filesystem adapters work — the
config.FSfamily - Use an afero filesystem — the equivalent bridge for an
afero.Fs - Backends & capabilities — the
config.FSinterface andLinkReader