我用眼镜蛇和蝰蛇写了一个简单的CLI工具。我最近一直在重构它以避免包全局变量,主要是因为事实证明很难使用例如cobra init
.
而不是。。。
var rootCmd = &cobra.Command{
...
}
func main() {
rootCmd.Execute()
}
我有更像的东西:
func NewCmdRoot() *cobra.Command {
cmd := &cobra.Command{
...
}
return cmd
}
func main() {
rootCmd := NewCmdRoot()
rootCmd.Execute()
}
这实际上效果很好,并且使测试变得更加容易 从一组干净的 CLI 选项开始。我遇到了一些 难以将蝰蛇整合到新计划中。 如果我只在乎 关于根命令,我可以在PersistentPreRun
中设置内容 命令,如下所示:
func initConfig(cmd *cobra.Command) {
config := viper.New()
rootCmd := cmd.Root()
config.BindPFlag("my-nifty-option", rootCmd.Flag("my-nifty-option")); err != nil {
// ...stuff happens here...
config.ReadInConfig()
// What happens next?
}
func NewCmdRoot() *cobra.Command {
cmd := &cobra.Command{
PersistentPreRun: func(cmd *cobra.Command, args []string) {
initConfig(cmd)
},
}
这种工作:只要我只对配置选项感兴趣 对应于眼镜蛇命令行选项,事情的工作方式为 预期。但是,如果我想访问config
变量本身怎么办?
我不确定config
如何在initConfig
方法,无需将其转换为包全局。我想要 实例化多个命令树的可能性,每个命令树都有 它是自己的隔离Viper配置对象,但我不清楚放在哪里 它。
眼镜蛇团队最近这样做了,请参阅 https://github.com/spf13/cobra/pull/1551
func TestFoo(t *testing.T){
root := &Command{
Use: "root",
PreRun: func(cmd *Command, args []string) {
ctx := context.WithValue(cmd.Context(), key{}, val)
cmd.SetContext(ctx)
},
Run: func(cmd *Command, args []string) {
fmt.Println(cmd.Context().Value(key{}))
},
}
ctx := context.WithValue(cmd.Context(), "key", "default")
root.ExecuteContext(ctx)
}