Source-verified agent skills for Go: Cobra command-line interfaces, Viper configuration, and Wails desktop apps, each backed by tested examples.
99
100%
Does it follow best practices?
Impact
99%
1.02xAverage score across 18 eval scenarios
Passed
No findings from the security scan
Behavior below is for Viper v1.21.0; each statement has a test in
examples/viper/behavior/.
WatchConfig starts a goroutine that watches the config file's directory.
When the file is written or created, it calls ReadInConfig on the same
instance and then the OnConfigChange hook. Know these before relying on it:
WatchConfig returns nothing. If the watcher cannot be registered, for
example because the file's directory does not exist, Viper logs the error
and the hook never runs. Load the file once before watching so a bad path
fails at startup.*viper.Viper has no method to stop a watcher.Skip watching in short-lived CLI commands.
Watch with an instance nobody reads, and on each change run the normal loader on a fresh instance. A bad edit is reported; the published snapshot stays:
// examples/viper/config/watch.go
func Watch(src Source, apply func(Config), report func(error)) error {
path, err := resolve(src)
if err != nil {
return err
}
if path == "" {
return errors.New("no config file to watch")
}
src.File, src.Dirs = path, nil // reload the file being watched, never a newer find
if _, err := Load(src); err != nil {
return err // WatchConfig only logs a failed registration
}
w := viper.New()
w.SetConfigFile(path)
w.OnConfigChange(func(fsnotify.Event) {
cfg, err := Load(src)
if err != nil {
report(err)
return
}
apply(cfg)
})
w.WatchConfig()
return nil
}apply runs on the watcher goroutine. Publish with an atomic.Pointer[Config],
a mutex, or a channel send. In a TUI, send the snapshot to the UI loop as a
message rather than changing the model from the watcher goroutine. The test in
examples/viper/config/config_test.go applies a valid edit, then checks that
an invalid one is reported and the previous snapshot survives. Other tests
there check that a file created later in a higher-priority directory does not
take over the reload, and that a missing file fails Watch instead of
returning nil.
When a program needs cancellation or a bounded lifetime, use its own
fsnotify watcher and call Load on events instead of WatchConfig.
WriteConfig, WriteConfigAs, and WriteConfigTo serialize every effective
setting, so defaults and env values (including secrets such as a token) land
in the file next to the file's own values. Comments in the original file are
lost. SafeWriteConfigAs only adds a check: it refuses an existing file with
viper.ConfigFileAlreadyExistsError.
For a command that edits the user's config, read the user's file into its own
document, change the intended keys, and write that back yourself. Writing a
temp file and renaming it survives crashes, but a running WatchConfig on
macOS stops at the rename. Do not rewrite files another tool owns,
such as ones generated by Nix or a configuration manager.
Sub("server") returns nil when the key is missing or holds a scalar.
Decode typed sections at the boundary instead of passing sub-instances
around.IsSet is true for a key with only a SetDefault; it does not mean the
user set anything. InConfig looks only at the file. ConfigFileUsed
returns the file that was read, including one found by discovery.config show --origin command, for example), record it in the
loader as you bind each layer, and redact secrets in the output.Remote key/value providers (github.com/spf13/viper/remote) are outside this
skill. Add one only for a real remote-settings requirement, and check its
dependencies, credentials, and retry behavior in the selected version.