asarchami/dotfiles/go-spf13-viper
- community
Checking that the SKILL.md file is still in the repository.
viper config resolution — layered precedence (flag > env > file > KV > default), env binding triple, BindPFlag, graceful ConfigFileNotFoundError, mapstructure unmarshal, Sub, hot reload, and test isolation. Use when adopting viper, wiring flags or env to config values, or debugging why a key resolves to the wrong source layer.
Install
npx vskill@latest install asarchami/dotfiles/go-spf13-viperSKILL.md
spf13/viper
Leading word: config. Viper answers one question — "what is the value of key X right now?" — by walking a fixed precedence pipeline (flag beats env beats file beats KV beats default). The discipline: know which layer every key comes from, bind each key once so all reachable layers stay visible, and treat a missing config file as a normal state, not a crash.
Steps
- Walk the precedence pipeline before writing code. Resolve each key's source layers in order: explicit
Set(), bound flag, env var, config file, KV remote, default. A key that "should" come from a file may be shadowed by an env var or a flag default.
Done when: for every key, you can name the highest-priority layer that may set it and the shadowing it must survive.
- Wire the env binding triple together. Set
SetEnvPrefix("MYAPP")to prevent collisions,SetEnvKeyReplacer(strings.NewReplacer(".", "_"))sodatabase.hostmaps toMYAPP_DATABASE_HOST, andAutomaticEnv(). Missing any one breaks nested key resolution silently.
Done when: every nested config key resolves from its env var with dots translated to underscores.
- Read the config file gracefully. Use
errors.As(err, ¬Found)onviper.ConfigFileNotFoundErrorand propagate only real errors — a missing file must not crash a run that works on flags and env alone.
Done when: a run with no config file starts cleanly, while genuinely corrupt files still fail loudly.
- Bind flags before `Execute()`. Call
viper.BindPFlag/BindPFlagsininit()orPersistentPreRunE— binding inRunEis too late because cobra parses flags before it runs.
Done when: every cobra flag the CLI exposes is bound to a viper key before command execution.
- Unmarshal into structs with `mapstructure` tags. Tag every field (
mapstructure:"max_conn"); implicit mapping silently misses nested and underscore-named fields. PreferUnmarshalKey("database", &dbCfg)overSub("database").Unmarshal— it avoids the nil-checkSubrequires when the key is missing.
Done when: every config struct field carries an explicit tag and no `Sub` result is used without a nil check.
- Isolate tests with `viper.New()`. Never use the global viper in tests — its state leaks across test cases and causes flaky ordering.
Done when: every test configures its own `viper.Viper` instance.*
- Handle hot reload with fsnotify's limits.
WatchConfig+OnConfigChangewatch inodes; editors that save via atomic rename (vim, neovim) replace the inode and the callback may not fire.
Done when: the reload path is verified with `echo >> config.yaml`, not editor saves.
Reference
The precedence pipeline
The pipeline is fixed and cannot be reordered.
Reading a config file
Env binding triple
Without SetEnvKeyReplacer, viper looks for MYAPP_DATABASE.HOST (dot preserved) and never matches.
Flag binding (the cobra seam)
Unmarshaling
Hot reload
Resources
- pkg.go.dev/github.com/spf13/viper · github.com/spf13/viper
- Install:
go get github.com/spf13/viper@latest
Deep references
- [sources-and-formats.md](./references/sources-and-formats.md) — supported formats, multi-path search,
MergeInConfig, remote KV (etcd/Consul) - [binding-and-env.md](./references/binding-and-env.md) —
BindEnv,AutomaticEnv,SetEnvPrefix,SetEnvKeyReplacer,AllowEmptyEnv, timing rules - [unmarshal.md](./references/unmarshal.md) —
Unmarshal,UnmarshalKey, mapstructure tags, customDecodeHooks (Duration, IP, slice) - [watch-and-reload.md](./references/watch-and-reload.md) —
WatchConfig,OnConfigChange, fsnotify caveats, atomic-rename trap, race-safe patterns - [testing-and-isolation.md](./references/testing-and-isolation.md) —
viper.New()per test,t.Setenvinteractions,Reset()limitations, snapshot/restore
Watch for
| Mistake | Fix | |
|---|---|---|
AutomaticEnv without SetEnvKeyReplacer | Add SetEnvKeyReplacer(strings.NewReplacer(".", "_")) before AutomaticEnv | |
No mapstructure tags on struct fields | Add mapstructure:"key_name" to every field | |
| Using global viper in tests | Create viper.New() per test | |
Missing ConfigFileNotFoundError check | errors.As(err, ¬Found) — propagate only non-not-found errors | |
Binding flags in RunE | Bind in init() or PersistentPreRunE, before cobra parses |
Cross-references
- → See
go-spf13-cobrafor the cobra side — flag definition and binding - → See
go-clifor general CLI architecture — project layout, exit codes, signal handling - → See
go-testingfor general Go testing patterns
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingdot_config/opencode/skills/go/go-spf13-viper/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/asarchami/dotfiles/go-spf13-viper)