Skip to content

Improve output flag binding#117

Merged
nathanjcochran merged 5 commits intomainfrom
nathan/output-flag-binding
Dec 1, 2025
Merged

Improve output flag binding#117
nathanjcochran merged 5 commits intomainfrom
nathan/output-flag-binding

Conversation

@nathanjcochran
Copy link
Member

@nathanjcochran nathanjcochran commented Nov 25, 2025

This follows up on an issue originally described here. Namely, we were previously having trouble binding subcommands' --output flags to viper, and were therefore doing it manually in the subcommands' RunE functions instead, like this :

if cmd.Flags().Changed("output") {
	cfg.Output = output
}

While that technically worked fine, it stood out as a weird workaround.

Turns out the problem was simply that we were trying to bind multiple *pflag.Flag instances to the same output key in viper, since we were binding the flags in the subcommand builder functions (which all run unconditionally, no matter which subcommand is actually being invoked). As a result, the viper.BindPFlag("output", ...) calls would overwrite each other, and only the last --output *pflag.Flag instance would actually end up being bound to viper.

This issue is apparently fairly common, and is described here: spf13/viper#233. A solution is recommended here: spf13/viper#233 (comment).

This PR updates the code to bind the --output flags to viper instead of the manual workaround, and follows through on the recommendation linked above - namely, it moves all of the viper.BindPFlag() calls into the subcommands' PreRunE functions (via a helper function that makes this a one-liner), so that only the flags for commands that are actually being invoked are bound to viper. That ensures that only the relevant --output flag is bound and takes effect.

CC @murrayju since you're the one who originally ran into this issue.

@nathanjcochran nathanjcochran self-assigned this Nov 25, 2025
Comment on lines +45 to +55
// Bind persistent flags to viper
// Use cmd.Flags() which includes inherited persistent flags from parents
if err := errors.Join(
viper.BindPFlag("debug", cmd.Flags().Lookup("debug")),
viper.BindPFlag("service_id", cmd.Flags().Lookup("service-id")),
viper.BindPFlag("analytics", cmd.Flags().Lookup("analytics")),
viper.BindPFlag("password_storage", cmd.Flags().Lookup("password-storage")),
viper.BindPFlag("color", cmd.Flags().Lookup("color")),
); err != nil {
return fmt.Errorf("failed to bind flags: %w", err)
}
Copy link
Member Author

@nathanjcochran nathanjcochran Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved these bind calls up into the root command's PersistentPreRunE, even though there wasn't really a problem with how these were being bound before (since they're global flags that always apply anyways). I figured it was more consistent to do it this way, and this should hopefully help encourage people to follow the new pattern of binding flags in the (Persistent)PreRunE functions instead of in the builder functions.

@nathanjcochran nathanjcochran marked this pull request as ready for review November 25, 2025 23:04
Copy link
Member

@murrayju murrayju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much improved!

@nathanjcochran nathanjcochran merged commit 6988f3a into main Dec 1, 2025
2 checks passed
@nathanjcochran nathanjcochran deleted the nathan/output-flag-binding branch December 1, 2025 22:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments