运用golang命令行工具手搓一个项目部署cli

221 阅读3分钟

记录学习一下golang中常用的命令行工具,并加以运用做一个轻量级的项目部署工具。

Go 中常用的命令行工具

标准库flag

package main

import (
    "flag"
    "fmt"
)

func main() {
    // 定义命令行参数
    name := flag.String("name", "默认值", "参数说明")
    age := flag.Int("age", 0, "年龄")
    isVerbose := flag.Bool("verbose", false, "是否显示详细信息")

    // 解析命令行参数
    flag.Parse()

    fmt.Printf("名字: %s\n", *name)
    fmt.Printf("年龄: %d\n", *age)
    fmt.Printf("详细模式: %v\n", *isVerbose)
}

Cobra包(较flag更推荐)

安装:

go get -u github.com/spf13/cobra

基本使用:

package main

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

func main() {
    var rootCmd = &cobra.Command{
        Use:   "myapp",
        Short: "我的命令行工具",
    }

    // 添加子命令
    var versionCmd = &cobra.Command{
        Use:   "version",
        Short: "打印版本号",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("v1.0.0")
        },
    }

    var serveCmd = &cobra.Command{
        Use:   "serve",
        Short: "启动服务",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("服务启动中...")
        },
    }

    rootCmd.AddCommand(versionCmd, serveCmd)
    rootCmd.Execute()
}

命令行参数解析包 flag 和 cobra,常用功能: 命令行自动补全、帮助信息生成、参数验证、环境变量支持、配置文件集成

urfave/cli

github.com/urfave/cli 是一个强大的命令行工具,urfave/cli 是一个功能丰富的命令行工具包,它:

  • 支持子命令

  • 提供丰富的参数类型

  • 支持命令别名

  • 可自定义帮助信息

  • 提供钩子函数

  • 支持命令分类

  • 提供完善的参数验证

这个包特别适合构建复杂的命令行应用,使用简单且功能强大。

看示例:

package main

import (
    "fmt"
    "log"
    "os"
    "github.com/urfave/cli/v2"
)

func main() {
    app := &cli.App{
        Name:  "任务管理器",
        Usage: "一个简单的任务管理CLI应用",
        Flags: []cli.Flag{
            &cli.StringFlag{
                Name:    "config",
                Aliases: []string{"c"},
                Usage:   "配置文件路径",
            },
            &cli.BoolFlag{
                Name:    "debug",
                Usage:   "启用调试模式",
                Value:   false,
            },
        },
        Commands: []*cli.Command{
            {
                Name:  "task",
                Usage: "任务管理",
                Subcommands: []*cli.Command{
                    {
                        Name:  "add",
                        Usage: "添加任务",
                        Flags: []cli.Flag{
                            &cli.StringFlag{
                                Name:     "name",
                                Aliases:  []string{"n"},
                                Usage:    "任务名称",
                                Required: true,
                            },
                        },
                        Action: func(c *cli.Context) error {
                            name := c.String("name")
                            fmt.Printf("添加任务: %s\n", name)
                            return nil
                        },
                    },
                    {
                        Name:  "list",
                        Usage: "列出所有任务",
                        Action: func(c *cli.Context) error {
                            fmt.Println("任务列表:")
                            return nil
                        },
                    },
                },
            },
        },
        Before: func(c *cli.Context) error {
            // 在命令执行前运行
            if c.Bool("debug") {
                fmt.Println("调试模式已启用")
            }
            return nil
        },
    }

    if err := app.Run(os.Args); err != nil {
        log.Fatal(err)
    }
}

做一个轻量级的项目部署工具

  • 实现思路:

项目的部署命令记录在配置文件中,通过命令行cli选择想部署的项目,执行部署并实时打印执行结果

  • 项目目录结构:

image.png

  • 核心代码
// main.go

package main

import (
	"os"

	"github.com/shennonggo/single-deploy/internal/deploy"
	"github.com/shennonggo/single-deploy/internal/utils"

	"github.com/urfave/cli/v2"
)

func main() {
	app := &cli.App{
		Name:  "single-deploy",
		Usage: "Single Project Deployment Tool",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "config",
				Aliases: []string{"c"},
				Value:   "configs/deploy-config.json",
				Usage:   "Configuration file path",
			},
		},
		Action: runDeploy,
	}

	if err := app.Run(os.Args); err != nil {
		utils.LogError("Deployment failed: %v", err)
		os.Exit(1)
	}
}

func runDeploy(c *cli.Context) error {
	return deploy.Start(c.String("config"))
}
// deploy.go

package deploy

import (
	"fmt"
	"time"

	"github.com/AlecAivazis/survey/v2"
	"github.com/briandowns/spinner"
	"github.com/shennonggo/single-deploy/internal/config"
	"github.com/shennonggo/single-deploy/internal/utils"
)

func Start(configPath string) error {
	// Load configuration
	cfg, err := config.LoadConfig(configPath)
	if err != nil {
		return err
	}

	if err := cfg.Validate(); err != nil {
		return err
	}

	// Select project
	project, err := selectProject(cfg.Projects)
	if err != nil {
		return err
	}

	// Create spinner
	s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
	s.Prefix = "🚀 "

	// Execute deployment steps
	steps := GetDeploySteps()
	for _, step := range steps {
		s.Suffix = fmt.Sprintf(" %s...", step.Name)
		s.Start()

		if err := step.Execute(project); err != nil {
			s.Stop()
			return fmt.Errorf("%s failed: %v", step.Name, err)
		}

		s.Stop()
		utils.LogSuccess("%s completed", step.Name)
	}

	utils.LogSuccess("Project %s deployed successfully!", project.Name)
	return nil
}

func selectProject(projects []config.Project) (config.Project, error) {
	var selected string
	options := make([]string, len(projects))
	projectMap := make(map[string]config.Project)

	for i, p := range projects {
		options[i] = p.Name
		projectMap[p.Name] = p
	}

	prompt := &survey.Select{
		Message: "Please select the project to deploy:",
		Options: options,
	}

	if err := survey.AskOne(prompt, &selected); err != nil {
		return config.Project{}, err
	}

	return projectMap[selected], nil
}

// deploy-config.json
{
  "projects": [
    {
      "name": "my-frontend",
      "path": "./projects/frontend",
      "type": "react",
      "gitRepo": "https://github.com/username/frontend-app.git",
      "gitBranch": "main",
      "buildCmd": "npm install && npm run build",
      "startCmd": "npm start",
      "healthCheck": {
        "url": "http://localhost:3000",
        "timeout": 30
      }
    },
    {
      "name": "my-backend",
      "path": "./projects/backend",
      "type": "golang",
      "gitRepo": "https://github.com/username/backend-service.git",
      "gitBranch": "main",
      "buildCmd": "go mod download && go build -o app",
      "startCmd": "./app",
      "healthCheck": {
        "url": "http://localhost:8080/health",
        "timeout": 60
      }
    }
  ]
}
  • 功能点介绍(形容词美化版)
  • 🚀 使用单个命令部署多个项目
  • 📦 支持不同项目类型(Node.js、React 等)
  • 🔄 集成 Git 进行源代码管理
  • 🏗️ 自定义构建和启动命令
  • 🏥 健康检查监控
  • 💻 跨平台支持(Windows、Linux、macOS)
  • 项目地址

github.com/shennonggo/…


种下代码,收获未来 Plant Code, Harvest Future