refactor: adopt Ghost's config and client loading patterns - #183
Conversation
| var configDir string | ||
| var debug bool | ||
| var serviceID string | ||
| var analytics bool | ||
| var analyticsEnabled bool | ||
| var passwordStorage string | ||
| var skipUpdateCheck bool | ||
| var colorFlag bool |
There was a problem hiding this comment.
I think these variables are all completely redundant. At least Claude tells me we could just use .StringP instead of .StringVarP and then these variables disappear. I mention this because there is always the theoretical mistake one can make by reading from the variable directly instead of from the config struct, which would bypass the viper load order.
Idk if that's also worth cleaning up.
There was a problem hiding this comment.
Great point. Addressed here: 34049b6.
Claude also noticed that the same is true for the various output flags defined for various commands - they don't need a local variable, because the intention is for the output format to always be read from the config, and the flag is just one way of setting the config value. That also uncovered a bug where tiger version was reading the local flag variable directly, and therefore not respecting the value in the config or env var. So that was fixed as well.
3c8d98e to
c43fc5a
Compare
main gained *config.Config on the connection helpers and moved command wiring to App.GetAll (#183), which lands as a silent break here: the merge is textually clean but the call sites stop compiling. Thread Config through ConnectableWaitArgs, take the client as api.ClientWithResponsesInterface to match WaitForServiceArgs, and read client/projectID from App.GetAll at the three call sites.
Reworks how Tiger CLI loads its configuration and API client, adopting several patterns from ghost. The public interface is unchanged — same commands, flags, config keys, and environment variables — this is all internals.
The starting point: config lived in a process-wide viper instance that code read from directly, and both the config and the API client were loaded several times per invocation. That made the precedence rules (flag > env > file > default) hard to reason about, let one command's flag bindings leak into another's, and produced at least one real bug.
Config loading
config.Load(flags)builds its own viper instance per call and unmarshals it into aConfig; nothing reads a process-wide instance anymore. Config is now just a value that gets passed around.tiger config setused to override env vars and flags. It wrote the config file and calledviper.Seton the global instance, which is a hard override — so for the rest of that process, the value it wrote beat anyTIGER_*env var or CLI flag.Set/Unset/Resetnow write the file and reload the struct through normal precedence.PreRunEhook that each command had to remember to add, and means a command-local flag like--outputbinds only for the commands that define it, so commands can no longer stomp each other's bindings.validateValue, andtiger config shownow goes through one helper instead of assembling a viper by hand.--config-diris now honored consistently for credential storage.Loading the config and client once (the
App)common.Appholds the config and API client; a wrapper around every command'sRunEloads them once and shares them through accessors. Commands previously caused 3–4 config loads and two credential/keyring reads per run.common.Configwrapper, which embedded*config.Configalongside the client and project ID — two different types named "Config", plus a lot ofcfg.Configunwrapping at call sites.PersistentPreRunE/PersistentPostRunEare gone. Loading, logging setup, color, the update check, and analytics now happen in one wrapper, in a readable order.--helpand completions no longer load anything. The commands cobra adds after wrapping (help,completion,__complete) skip the lifecycle entirely, so help output and tab completion no longer read the config file or touch the system keyring. The two completions that genuinely need the API opt in explicitly.version_checkoff.TIGER_EXPERIMENTALis read once into the App, instead of separately by the CLI and the MCP server.Tests
Apphas a client-factory seam for exactly that. A follow-up PR will move the test machinery over to it.~/.config/tiger/config.yamlof whoever ran them. Tests now always get an isolated config directory.Behavior worth knowing about while reviewing
config reset/unsetno longer mask env vars for the remainder of the process — that was a side effect of the override bug above; the effective value now re-resolves normally.tiger config showno longer creates the config directory as a side effect.new_passwordconfig-file key is no longer read. The--new-passwordflag andTIGER_NEW_PASSWORDwork as before.tiger auth logout's analytics event is now attributed to the user who logged out, instead of going out unauthenticated.