cobra-modularity

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cobra Modular CLI Architecture

Cobra模块化CLI架构

Build scalable, maintainable CLI applications using Cobra with modular command registration patterns.
使用Cobra的模块化命令注册模式构建可扩展、易维护的CLI应用程序。

Your Role: CLI Architect

你的角色:CLI架构师

You help structure CLI applications with clean, modular architecture. You:
Design modular command structure - Self-contained command modules ✅ Implement Register pattern - Commands register themselves ✅ Handle flags properly - Persistent vs local, parsing, validation ✅ Structure subcommands - Nested command hierarchies ✅ Apply Cobra idioms - RunE for errors, PreRun hooks, etc. ✅ Follow project patterns - Use existing module conventions
Do NOT centralize commands - Keep modules self-contained ❌ Do NOT modify root unnecessarily - Add via registration only ❌ Do NOT ignore errors - Use RunE, not Run
你需要协助构建架构清晰、模块化的CLI应用程序。你需要:
设计模块化命令结构 - 独立的命令模块 ✅ 实现注册模式 - 命令自行完成注册 ✅ 正确处理标志 - 持久化标志与本地标志、解析、验证 ✅ 构建子命令结构 - 嵌套命令层级 ✅ 应用Cobra惯用写法 - 使用RunE处理错误、PreRun钩子等 ✅ 遵循项目模式 - 采用现有模块约定
不要集中管理命令 - 保持模块独立 ❌ 不要不必要地修改根命令 - 仅通过注册方式添加 ❌ 不要忽略错误 - 使用RunE,而非Run

Core Principles

核心原则

1. Modular Command Registration

1. 模块化命令注册

Commands live in their own packages and register with parent commands.
✅ GOOD - Self-registering module:
go
// modules/demo/cmd.go
package demo

import "github.com/spf13/cobra"

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "demo",
        Short: "Demo commands",
    }
    parent.AddCommand(cmd)

    // Register subcommands
    spinner.Register(cmd)
    list.Register(cmd)
}
❌ BAD - Centralized registration:
go
// cmd/root.go - DON'T do this
func init() {
    RootCmd.AddCommand(demoCmd)
    RootCmd.AddCommand(listCmd)
    RootCmd.AddCommand(spinnerCmd)
    // Root knows too much
}
命令存放在独立包中,并向父命令完成注册。
✅ 推荐 - 自注册模块:
go
// modules/demo/cmd.go
package demo

import "github.com/spf13/cobra"

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "demo",
        Short: "Demo commands",
    }
    parent.AddCommand(cmd)

    // 注册子命令
    spinner.Register(cmd)
    list.Register(cmd)
}
❌ 不推荐 - 集中式注册:
go
// cmd/root.go - 不要这样做
func init() {
    RootCmd.AddCommand(demoCmd)
    RootCmd.AddCommand(listCmd)
    RootCmd.AddCommand(spinnerCmd)
    // 根命令耦合性过高
}

2. Command Structure Pattern

2. 命令结构模式

Standard module layout:
modules/demo/
├── cmd.go              # Register function
├── spinner/
│   ├── cmd.go         # spinner.Register()
│   └── spinner.go     # Implementation
└── list/
    ├── cmd.go         # list.Register()
    └── list.go        # Implementation
Benefits:
  • Module is self-contained
  • Easy to add/remove commands
  • No changes to root when adding modules
  • Testable in isolation
标准模块布局:
modules/demo/
├── cmd.go              # 注册函数
├── spinner/
│   ├── cmd.go         # spinner.Register()
│   └── spinner.go     # 实现代码
└── list/
    ├── cmd.go         # list.Register()
    └── list.go        # 实现代码
优势:
  • 模块独立完整
  • 易于添加/移除命令
  • 添加模块时无需修改根命令
  • 可独立测试

3. Error Handling

3. 错误处理

Use RunE (not Run):
✅ GOOD:
go
cmd := &cobra.Command{
    Use:   "fetch",
    Short: "Fetch data",
    RunE:  run,  // Returns error
}

func run(cmd *cobra.Command, args []string) error {
    if err := doWork(); err != nil {
        return fmt.Errorf("fetch failed: %w", err)
    }
    return nil
}
❌ BAD:
go
cmd := &cobra.Command{
    Run: func(cmd *cobra.Command, args []string) {
        doWork()  // Error ignored
    },
}
使用RunE(而非Run):
✅ 推荐:
go
cmd := &cobra.Command{
    Use:   "fetch",
    Short: "Fetch data",
    RunE:  run,  // 返回错误
}

func run(cmd *cobra.Command, args []string) error {
    if err := doWork(); err != nil {
        return fmt.Errorf("fetch failed: %w", err)
    }
    return nil
}
❌ 不推荐:
go
cmd := &cobra.Command{
    Run: func(cmd *cobra.Command, args []string) {
        doWork()  // 错误被忽略
    },
}

Cobra Command Structure

Cobra命令结构

Basic Command

基础命令

go
package mycommand

import (
    "github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "mycommand",
        Short: "Short description",
        Long:  `Long description with examples`,
        RunE:  run,
    }
    parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
    // Implementation
    return nil
}
go
package mycommand

import (
    "github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "mycommand",
        Short: "简短描述",
        Long:  `带示例的详细描述`,
        RunE:  run,
    }
    parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
    // 实现代码
    return nil
}

With Flags

带标志的命令

Local flags (command-specific):
go
var (
    flagName   string
    flagCount  int
    flagVerbose bool
)

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "process",
        Short: "Process data",
        RunE:  run,
    }

    // String flag with shorthand
    cmd.Flags().StringVarP(&flagName, "name", "n", "", "Name (required)")
    cmd.MarkFlagRequired("name")

    // Int flag
    cmd.Flags().IntVarP(&flagCount, "count", "c", 10, "Count")

    // Bool flag
    cmd.Flags().BoolVarP(&flagVerbose, "verbose", "v", false, "Verbose output")

    parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
    // Use flagName, flagCount, flagVerbose
    return nil
}
Persistent flags (inherited by subcommands):
go
func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "server",
        Short: "Server commands",
    }

    // Available to all subcommands
    cmd.PersistentFlags().StringP("config", "c", "", "Config file")

    parent.AddCommand(cmd)

    // Register subcommands
    start.Register(cmd)  // Can access --config
    stop.Register(cmd)   // Can access --config
}
本地标志(命令专属):
go
var (
    flagName   string
    flagCount  int
    flagVerbose bool
)

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "process",
        Short: "处理数据",
        RunE:  run,
    }

    // 带简写的字符串标志
    cmd.Flags().StringVarP(&flagName, "name", "n", "", "名称(必填)")
    cmd.MarkFlagRequired("name")

    // 整数标志
    cmd.Flags().IntVarP(&flagCount, "count", "c", 10, "数量")

    // 布尔标志
    cmd.Flags().BoolVarP(&flagVerbose, "verbose", "v", false, "详细输出")

    parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
    // 使用flagName, flagCount, flagVerbose
    return nil
}
持久化标志(被子命令继承):
go
func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "server",
        Short: "服务器命令",
    }

    // 所有子命令均可使用
    cmd.PersistentFlags().StringP("config", "c", "", "配置文件")

    parent.AddCommand(cmd)

    // 注册子命令
    start.Register(cmd)  // 可访问--config
    stop.Register(cmd)   // 可访问--config
}

With Arguments

带参数的命令

Exact args:
go
cmd := &cobra.Command{
    Use:   "delete <id>",
    Short: "Delete item by ID",
    Args:  cobra.ExactArgs(1),  // Requires exactly 1 arg
    RunE:  run,
}

func run(cmd *cobra.Command, args []string) error {
    id := args[0]
    return deleteItem(id)
}
Range of args:
go
Args: cobra.RangeArgs(1, 3),  // 1 to 3 args
Args: cobra.MinimumNArgs(1),  // At least 1 arg
Args: cobra.MaximumNArgs(2),  // At most 2 args
Custom validation:
go
Args: func(cmd *cobra.Command, args []string) error {
    if len(args) != 1 {
        return fmt.Errorf("requires exactly one arg")
    }
    if !isValidID(args[0]) {
        return fmt.Errorf("invalid ID: %s", args[0])
    }
    return nil
},
固定数量参数:
go
cmd := &cobra.Command{
    Use:   "delete <id>",
    Short: "按ID删除项",
    Args:  cobra.ExactArgs(1),  // 要求恰好1个参数
    RunE:  run,
}

func run(cmd *cobra.Command, args []string) error {
    id := args[0]
    return deleteItem(id)
}
参数范围:
go
Args: cobra.RangeArgs(1, 3),  // 1到3个参数
Args: cobra.MinimumNArgs(1),  // 至少1个参数
Args: cobra.MaximumNArgs(2),  // 最多2个参数
自定义验证:
go
Args: func(cmd *cobra.Command, args []string) error {
    if len(args) != 1 {
        return fmt.Errorf("需要恰好1个参数")
    }
    if !isValidID(args[0]) {
        return fmt.Errorf("无效ID: %s", args[0])
    }
    return nil
},

Module Patterns

模块模式

Demo Module Pattern (CLY)

示例模块模式(CLY)

Parent command -
modules/demo/cmd.go
:
go
package demo

import (
    "github.com/spf13/cobra"
    spinner "github.com/yurifrl/cly/modules/demo/spinner"
)

var DemoCmd = &cobra.Command{
    Use:   "demo",
    Short: "Demo TUI components",
}

func Register(parent *cobra.Command) {
    parent.AddCommand(DemoCmd)
}

func init() {
    // Register all demo subcommands
    spinner.Register(DemoCmd)
    // ... more demos
}
Subcommand -
modules/demo/spinner/cmd.go
:
go
package spinner

import (
    tea "github.com/charmbracelet/bubbletea"
    "github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "spinner",
        Short: "Spinner demo",
        RunE:  run,
    }
    parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
    p := tea.NewProgram(initialModel())
    if _, err := p.Run(); err != nil {
        return err
    }
    return nil
}
父命令 -
modules/demo/cmd.go
:
go
package demo

import (
    "github.com/spf13/cobra"
    spinner "github.com/yurifrl/cly/modules/demo/spinner"
)

var DemoCmd = &cobra.Command{
    Use:   "demo",
    Short: "Demo TUI components",
}

func Register(parent *cobra.Command) {
    parent.AddCommand(DemoCmd)
}

func init() {
    // 注册所有示例子命令
    spinner.Register(DemoCmd)
    // ... 更多示例
}
子命令 -
modules/demo/spinner/cmd.go
:
go
package spinner

import (
    tea "github.com/charmbracelet/bubbletea"
    "github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "spinner",
        Short: "Spinner demo",
        RunE:  run,
    }
    parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
    p := tea.NewProgram(initialModel())
    if _, err := p.Run(); err != nil {
        return err
    }
    return nil
}

Utility Module Pattern (CLY)

工具模块模式(CLY)

Standalone command -
modules/uuid/cmd.go
:
go
package uuid

import (
    tea "github.com/charmbracelet/bubbletea"
    "github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "uuid",
        Short: "Generate UUIDs",
        Long:  "Interactive UUID generator with history",
        RunE:  run,
    }
    parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
    p := tea.NewProgram(initialModel())
    _, err := p.Run()
    return err
}
Registered in root -
cmd/root.go
:
go
import (
    "github.com/yurifrl/cly/modules/uuid"
    "github.com/yurifrl/cly/modules/demo"
)

func init() {
    uuid.Register(RootCmd)
    demo.Register(RootCmd)
}
独立命令 -
modules/uuid/cmd.go
:
go
package uuid

import (
    tea "github.com/charmbracelet/bubbletea"
    "github.com/spf13/cobra"
)

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "uuid",
        Short: "生成UUID",
        Long:  "带历史记录的交互式UUID生成器",
        RunE:  run,
    }
    parent.AddCommand(cmd)
}

func run(cmd *cobra.Command, args []string) error {
    p := tea.NewProgram(initialModel())
    _, err := p.Run()
    return err
}
在根命令中注册 -
cmd/root.go
:
go
import (
    "github.com/yurifrl/cly/modules/uuid"
    "github.com/yurifrl/cly/modules/demo"
)

func init() {
    uuid.Register(RootCmd)
    demo.Register(RootCmd)
}

Advanced Patterns

进阶模式

PreRun Hooks

PreRun钩子

Validate before run:
go
cmd := &cobra.Command{
    Use:  "deploy",
    PreRunE: func(cmd *cobra.Command, args []string) error {
        // Validation
        if !fileExists(configFile) {
            return fmt.Errorf("config not found: %s", configFile)
        }
        return nil
    },
    RunE: run,
}
Setup before run:
go
PreRunE: func(cmd *cobra.Command, args []string) error {
    // Setup database connection
    db, err = connectDB()
    return err
},
运行前验证:
go
cmd := &cobra.Command{
    Use:  "deploy",
    PreRunE: func(cmd *cobra.Command, args []string) error {
        // 验证逻辑
        if !fileExists(configFile) {
            return fmt.Errorf("未找到配置文件: %s", configFile)
        }
        return nil
    },
    RunE: run,
}
运行前初始化:
go
PreRunE: func(cmd *cobra.Command, args []string) error {
    // 建立数据库连接
    db, err = connectDB()
    return err
},

PostRun Hooks

PostRun钩子

Cleanup after run:
go
PostRun: func(cmd *cobra.Command, args []string) {
    // Cleanup
    db.Close()
    tempFile.Remove()
},
运行后清理:
go
PostRun: func(cmd *cobra.Command, args []string) {
    // 清理操作
    db.Close()
    tempFile.Remove()
},

Persistent PreRun (Inherited)

持久化PreRun(可继承)

go
cmd := &cobra.Command{
    Use: "api",
    PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
        // Runs before ALL subcommands
        return loadConfig()
    },
}
go
cmd := &cobra.Command{
    Use: "api",
    PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
        // 在所有子命令运行前执行
        return loadConfig()
    },
}

Flag Dependencies

标志依赖

Require flag if another present:
go
cmd.Flags().String("format", "", "Output format")
cmd.Flags().String("output", "", "Output file")

cmd.MarkFlagsRequiredTogether("format", "output")
Mutually exclusive flags:
go
cmd.Flags().Bool("json", false, "JSON output")
cmd.Flags().Bool("yaml", false, "YAML output")

cmd.MarkFlagsMutuallyExclusive("json", "yaml")
存在一个标志时需要另一个标志:
go
cmd.Flags().String("format", "", "输出格式")
cmd.Flags().String("output", "", "输出文件")

cmd.MarkFlagsRequiredTogether("format", "output")
互斥标志:
go
cmd.Flags().Bool("json", false, "JSON输出")
cmd.Flags().Bool("yaml", false, "YAML输出")

cmd.MarkFlagsMutuallyExclusive("json", "yaml")

Subcommand Groups

子命令分组

go
parent := &cobra.Command{
    Use:   "api",
    Short: "API commands",
}

// Group 1
parent.AddGroup(&cobra.Group{
    ID:    "server",
    Title: "Server Commands:",
})

cmd1 := &cobra.Command{
    Use:     "start",
    GroupID: "server",
}

cmd2 := &cobra.Command{
    Use:     "stop",
    GroupID: "server",
}

parent.AddCommand(cmd1, cmd2)
go
parent := &cobra.Command{
    Use:   "api",
    Short: "API命令",
}

// 分组1
parent.AddGroup(&cobra.Group{
    ID:    "server",
    Title: "服务器命令:",
})

cmd1 := &cobra.Command{
    Use:     "start",
    GroupID: "server",
}

cmd2 := &cobra.Command{
    Use:     "stop",
    GroupID: "server",
}

parent.AddCommand(cmd1, cmd2)

Configuration Integration

配置集成

With Viper

与Viper集成

go
import (
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var cfgFile string

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "server",
        Short: "Run server",
        PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
            return initConfig()
        },
        RunE: run,
    }

    cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "Config file")

    // Bind flag to viper
    viper.BindPFlag("config", cmd.PersistentFlags().Lookup("config"))

    parent.AddCommand(cmd)
}

func initConfig() error {
    if cfgFile != "" {
        viper.SetConfigFile(cfgFile)
    } else {
        viper.SetConfigName("config")
        viper.SetConfigType("yaml")
        viper.AddConfigPath(".")
        viper.AddConfigPath("$HOME/.myapp")
    }

    viper.AutomaticEnv()
    return viper.ReadInConfig()
}

func run(cmd *cobra.Command, args []string) error {
    port := viper.GetInt("server.port")
    // Use config
    return nil
}
go
import (
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var cfgFile string

func Register(parent *cobra.Command) {
    cmd := &cobra.Command{
        Use:   "server",
        Short: "启动服务器",
        PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
            return initConfig()
        },
        RunE: run,
    }

    cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件")

    // 将标志绑定到Viper
    viper.BindPFlag("config", cmd.PersistentFlags().Lookup("config"))

    parent.AddCommand(cmd)
}

func initConfig() error {
    if cfgFile != "" {
        viper.SetConfigFile(cfgFile)
    } else {
        viper.SetConfigName("config")
        viper.SetConfigType("yaml")
        viper.AddConfigPath(".")
        viper.AddConfigPath("$HOME/.myapp")
    }

    viper.AutomaticEnv()
    return viper.ReadInConfig()
}

func run(cmd *cobra.Command, args []string) error {
    port := viper.GetInt("server.port")
    // 使用配置
    return nil
}

Best Practices

最佳实践

1. Keep Commands Focused

1. 保持命令职责单一

One responsibility per command:
go
// ✅ GOOD
cly uuid              # Generate UUIDs
cly demo spinner      # Show spinner demo

// ❌ BAD
cly utils  # Does everything
每个命令对应一个职责:
go
// ✅ 推荐
cly uuid              # 生成UUID
cly demo spinner      # 展示加载动画示例

// ❌ 不推荐
cly utils  # 包含所有功能

2. Use Meaningful Names

2. 使用有意义的命令名称

go
// ✅ GOOD
Use: "generate",
Use: "list-users",
Use: "deploy-app",

// ❌ BAD
Use: "do",
Use: "run",
Use: "execute",
go
// ✅ 推荐
Use: "generate",
Use: "list-users",
Use: "deploy-app",

// ❌ 不推荐
Use: "do",
Use: "run",
Use: "execute",

3. Provide Good Help

3. 提供完善的帮助信息

go
cmd := &cobra.Command{
    Use:   "deploy <environment>",
    Short: "Deploy application",
    Long: `Deploy the application to specified environment.

Environments: dev, staging, prod

Examples:
  cly deploy dev
  cly deploy prod --version v1.2.3`,
    Example: `  cly deploy dev
  cly deploy prod --version v1.2.3`,
}
go
cmd := &cobra.Command{
    Use:   "deploy <environment>",
    Short: "部署应用",
    Long: `将应用部署到指定环境。

支持环境: dev, staging, prod

示例:
  cly deploy dev
  cly deploy prod --version v1.2.3`,
    Example: `  cly deploy dev
  cly deploy prod --version v1.2.3`,
}

4. Validate Early

4. 提前验证

go
PreRunE: func(cmd *cobra.Command, args []string) error {
    // Validate flags
    if port < 1 || port > 65535 {
        return fmt.Errorf("invalid port: %d", port)
    }

    // Check prerequisites
    if !commandExists("docker") {
        return fmt.Errorf("docker not found")
    }

    return nil
},
go
PreRunE: func(cmd *cobra.Command, args []string) error {
    // 验证标志
    if port < 1 || port > 65535 {
        return fmt.Errorf("无效端口: %d", port)
    }

    // 检查前置依赖
    if !commandExists("docker") {
        return fmt.Errorf("未找到docker")
    }

    return nil
},

5. Handle Interrupts

5. 处理中断信号

go
import (
    "context"
    "os/signal"
    "syscall"
)

func run(cmd *cobra.Command, args []string) error {
    ctx, stop := signal.NotifyContext(
        context.Background(),
        os.Interrupt,
        syscall.SIGTERM,
    )
    defer stop()

    return runWithContext(ctx)
}
go
import (
    "context"
    "os/signal"
    "syscall"
)

func run(cmd *cobra.Command, args []string) error {
    ctx, stop := signal.NotifyContext(
        context.Background(),
        os.Interrupt,
        syscall.SIGTERM,
    )
    defer stop()

    return runWithContext(ctx)
}

Testing Commands

命令测试

Test Registration

测试注册逻辑

go
func TestRegister(t *testing.T) {
    parent := &cobra.Command{Use: "root"}
    Register(parent)

    require.Len(t, parent.Commands(), 1)
    require.Equal(t, "mycommand", parent.Commands()[0].Use)
}
go
func TestRegister(t *testing.T) {
    parent := &cobra.Command{Use: "root"}
    Register(parent)

    require.Len(t, parent.Commands(), 1)
    require.Equal(t, "mycommand", parent.Commands()[0].Use)
}

Test Command Execution

测试命令执行

go
func TestRun(t *testing.T) {
    cmd := &cobra.Command{
        Use:  "test",
        RunE: run,
    }

    cmd.SetArgs([]string{"arg1", "arg2"})

    err := cmd.Execute()
    require.NoError(t, err)
}
go
func TestRun(t *testing.T) {
    cmd := &cobra.Command{
        Use:  "test",
        RunE: run,
    }

    cmd.SetArgs([]string{"arg1", "arg2"})

    err := cmd.Execute()
    require.NoError(t, err)
}

Test with Flags

带标志的命令测试

go
func TestRunWithFlags(t *testing.T) {
    cmd := &cobra.Command{
        Use:  "test",
        RunE: run,
    }

    var flagValue string
    cmd.Flags().StringVar(&flagValue, "flag", "", "test flag")

    cmd.SetArgs([]string{"--flag", "value"})

    err := cmd.Execute()
    require.NoError(t, err)
    require.Equal(t, "value", flagValue)
}
go
func TestRunWithFlags(t *testing.T) {
    cmd := &cobra.Command{
        Use:  "test",
        RunE: run,
    }

    var flagValue string
    cmd.Flags().StringVar(&flagValue, "flag", "", "test flag")

    cmd.SetArgs([]string{"--flag", "value"})

    err := cmd.Execute()
    require.NoError(t, err)
    require.Equal(t, "value", flagValue)
}

Common Pitfalls

常见陷阱

❌ Using Run instead of RunE:
go
Run: func(cmd *cobra.Command, args []string) {
    // Can't return errors!
    doWork()
}
✅ Use RunE:
go
RunE: func(cmd *cobra.Command, args []string) error {
    return doWork()
}
❌ Not validating args:
go
RunE: func(cmd *cobra.Command, args []string) error {
    id := args[0]  // Panic if no args!
    return nil
}
✅ Validate with Args:
go
cmd := &cobra.Command{
    Args: cobra.ExactArgs(1),
    RunE: run,
}
❌ Centralizing all commands:
go
// root.go
RootCmd.AddCommand(cmd1)
RootCmd.AddCommand(cmd2)
RootCmd.AddCommand(cmd3)
// Tight coupling
✅ Module registration:
go
// Each module registers itself
module1.Register(RootCmd)
module2.Register(RootCmd)
❌ 使用Run而非RunE:
go
Run: func(cmd *cobra.Command, args []string) {
    // 无法返回错误!
    doWork()
}
✅ 使用RunE:
go
RunE: func(cmd *cobra.Command, args []string) error {
    return doWork()
}
❌ 不验证参数:
go
RunE: func(cmd *cobra.Command, args []string) error {
    id := args[0]  // 无参数时会 panic!
    return nil
}
✅ 使用Args字段验证:
go
cmd := &cobra.Command{
    Args: cobra.ExactArgs(1),
    RunE: run,
}
❌ 集中管理所有命令:
go
// root.go
RootCmd.AddCommand(cmd1)
RootCmd.AddCommand(cmd2)
RootCmd.AddCommand(cmd3)
// 耦合性过高
✅ 模块注册方式:
go
// 每个模块自行注册
module1.Register(RootCmd)
module2.Register(RootCmd)

Checklist

检查清单

  • Command uses RunE (not Run)
  • Register() function for modularity
  • Args validated with Args field
  • Flags bound to variables
  • Required flags marked
  • Good Short description
  • Detailed Long description
  • Examples provided
  • Errors wrapped with context
  • Tests for command execution
  • 命令使用RunE(而非Run)
  • 提供Register()函数实现模块化
  • 通过Args字段验证参数
  • 标志已绑定到变量
  • 必填标志已标记
  • 包含清晰的Short描述
  • 包含详细的Long描述
  • 提供使用示例
  • 错误信息包含上下文
  • 包含命令执行测试

Reference

参考

CLY Project Structure:
cmd/
└── root.go              # Root command, imports modules

modules/
├── demo/
│   ├── cmd.go          # demo.Register()
│   └── spinner/
│       └── cmd.go      # spinner.Register()
└── uuid/
    └── cmd.go          # uuid.Register()
Pattern: Each module registers itself, no central registration.
CLY项目结构:
cmd/
└── root.go              # 根命令,导入模块

modules/
├── demo/
│   ├── cmd.go          # demo.Register()
│   └── spinner/
│       └── cmd.go      # spinner.Register()
└── uuid/
    └── cmd.go          # uuid.Register()
模式: 每个模块自行注册,无需集中管理。

Resources

资源