使用Golang和Cobra框架开发命令行应用,请求代码审查协助

使用Golang和Cobra框架开发命令行应用,请求代码审查协助 我正在编写我的第一个开源项目,希望能为Cyberduck社区提供帮助,并希望在此分享以获取大家的意见。

该项目使用 cobra 编写。这里是仓库链接。我需要在README和整体文档方面做一些额外的工作。

提前感谢您的任何反馈!

1 回复

更多关于使用Golang和Cobra框架开发命令行应用,请求代码审查协助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


以下是针对你使用Cobra框架开发的命令行应用的代码审查要点:

项目结构优化建议

// cmd/root.go 示例改进
var cfgFile string

var rootCmd = &cobra.Command{
    Use:   "cyberduck-s3-config",
    Short: "Manage Cyberduck S3 profiles",
    Long: `A CLI tool to create and manage Cyberduck S3 connection profiles
with support for AWS credentials and configuration.`,
    PersistentPreRun: func(cmd *cobra.Command, args []string) {
        // 统一的初始化逻辑
        initConfig()
    },
}

func init() {
    cobra.OnInitialize(initConfig)
    rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", 
        "config file (default is $HOME/.cyberduck-s3-config.yaml)")
}

命令组织改进

// cmd/create.go - 创建命令示例
var createCmd = &cobra.Command{
    Use:   "create [profile-name]",
    Short: "Create a new S3 profile",
    Args:  cobra.ExactArgs(1),
    RunE: func(cmd *cobra.Command, args []string) error {
        profileName := args[0]
        
        // 验证输入
        if err := validateProfileName(profileName); err != nil {
            return fmt.Errorf("invalid profile name: %w", err)
        }
        
        // 业务逻辑
        return createProfile(profileName)
    },
}

func init() {
    createCmd.Flags().StringP("access-key", "a", "", "AWS Access Key ID")
    createCmd.Flags().StringP("secret-key", "s", "", "AWS Secret Access Key")
    createCmd.Flags().StringP("region", "r", "us-east-1", "AWS Region")
    createCmd.MarkFlagRequired("access-key")
    createCmd.MarkFlagRequired("secret-key")
    
    rootCmd.AddCommand(createCmd)
}

错误处理改进

// 统一的错误处理模式
func createProfile(name string) error {
    // 检查配置文件是否存在
    if _, err := os.Stat(getProfilePath(name)); err == nil {
        return fmt.Errorf("profile '%s' already exists", name)
    }
    
    // 创建配置
    config := ProfileConfig{
        Name:      name,
        CreatedAt: time.Now(),
    }
    
    // 序列化并保存
    data, err := yaml.Marshal(config)
    if err != nil {
        return fmt.Errorf("failed to marshal config: %w", err)
    }
    
    if err := os.WriteFile(getProfilePath(name), data, 0644); err != nil {
        return fmt.Errorf("failed to write config file: %w", err)
    }
    
    return nil
}

配置文件管理示例

// config/config.go
package config

import (
    "os"
    "path/filepath"
    
    "gopkg.in/yaml.v3"
)

type ProfileConfig struct {
    Name       string    `yaml:"name"`
    AccessKey  string    `yaml:"access_key,omitempty"`
    SecretKey  string    `yaml:"secret_key,omitempty"`
    Region     string    `yaml:"region,omitempty"`
    Endpoint   string    `yaml:"endpoint,omitempty"`
    CreatedAt  time.Time `yaml:"created_at"`
}

func LoadProfile(name string) (*ProfileConfig, error) {
    path := getProfilePath(name)
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, err
    }
    
    var config ProfileConfig
    if err := yaml.Unmarshal(data, &config); err != nil {
        return nil, err
    }
    
    return &config, nil
}

func getProfilePath(name string) string {
    configDir, _ := os.UserConfigDir()
    return filepath.Join(configDir, "cyberduck-s3-config", name+".yaml")
}

测试示例

// cmd/create_test.go
func TestCreateCommand(t *testing.T) {
    tests := []struct {
        name        string
        args        []string
        flags       map[string]string
        expectError bool
    }{
        {
            name: "valid creation",
            args: []string{"test-profile"},
            flags: map[string]string{
                "access-key": "AKIAEXAMPLE",
                "secret-key": "secret",
            },
            expectError: false,
        },
        {
            name:        "missing required flags",
            args:        []string{"test-profile"},
            flags:       map[string]string{},
            expectError: true,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            cmd := createCmd
            // 测试逻辑
        })
    }
}

构建脚本示例

# Makefile
BINARY_NAME=cyberduck-s3-config
VERSION=0.1.0

build:
    go build -o bin/$(BINARY_NAME) -ldflags "-X main.version=$(VERSION)" ./cmd

test:
    go test ./...

install:
    go install ./cmd

release:
    GOOS=linux GOARCH=amd64 go build -o bin/$(BINARY_NAME)-linux-amd64 ./cmd
    GOOS=darwin GOARCH=amd64 go build -o bin/$(BINARY_NAME)-darwin-amd64 ./cmd
    GOOS=windows GOARCH=amd64 go build -o bin/$(BINARY_NAME)-windows-amd64.exe ./cmd

这些示例展示了Cobra最佳实践,包括命令组织、错误处理、配置管理和测试策略。项目结构清晰,遵循了Go社区的标准约定。

回到顶部