Go语言 眼镜蛇:不使用包全局变量就可以为子命令提供上下文?

osh3o9ms  于 2023-01-03  发布在  Go
关注(0)|答案(1)|浏览(110)

我用cobraviper编写了一个简单的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选项开始。我在将Viper集成到新方案中时遇到了一些困难。如果我只关心root命令,我可以在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)
    },
  }

这是一种工作方式:只要我只对与Cobra命令行选项对应的配置选项感兴趣,事情就会按预期工作,但如果我想访问config变量本身呢?
我不确定如何在initConfig方法之外公开config变量而不将其转换为包全局变量。我希望能够示例化多个命令树,每个命令树都有自己独立的Viper配置对象,但我不清楚将其放在哪里。

bq8i3lrv

bq8i3lrv1#

cobra团队最近已经完成了这项工作,请参见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)
}

相关问题