129 lines
3.8 KiB
Go
129 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// GlobalOptions 全局公共参数,所有子命令共享
|
|
type GlobalOptions struct {
|
|
Verbose bool // 详细日志模式
|
|
Config string // 全局配置文件
|
|
}
|
|
|
|
// SubCommand 通用子命令抽象结构体
|
|
type SubCommand struct {
|
|
Name string
|
|
Description string
|
|
Flags *flag.FlagSet
|
|
Run func([]string, GlobalOptions) error
|
|
}
|
|
|
|
// 提前声明变量,避免初始化循环引用
|
|
var commands map[string]*SubCommand
|
|
|
|
// init 初始化子命令,将 FlagSet 的绑定与执行逻辑分离
|
|
func init() {
|
|
// --- 初始化 build 命令 ---
|
|
buildCmd := &SubCommand{
|
|
Name: "build",
|
|
Description: "编译构建项目",
|
|
Flags: flag.NewFlagSet("build", flag.ContinueOnError),
|
|
}
|
|
var buildOutput string
|
|
// 在外部绑定参数,而不是在 Run 里面重新创建 FlagSet
|
|
buildCmd.Flags.StringVar(&buildOutput, "output", "./dist", "项目构建输出目录")
|
|
buildCmd.Run = func(args []string, global GlobalOptions) error {
|
|
if err := buildCmd.Flags.Parse(args); err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("[构建成功] 输出目录:%s,全局详细日志:%t,全局配置:%s\n",
|
|
buildOutput, global.Verbose, global.Config)
|
|
return nil
|
|
}
|
|
|
|
// --- 初始化 deploy 命令 ---
|
|
deployCmd := &SubCommand{
|
|
Name: "deploy",
|
|
Description: "部署项目到环境",
|
|
Flags: flag.NewFlagSet("deploy", flag.ContinueOnError),
|
|
}
|
|
var deployEnv string
|
|
deployCmd.Flags.StringVar(&deployEnv, "env", "dev", "部署环境 dev/test/prod")
|
|
deployCmd.Run = func(args []string, global GlobalOptions) error {
|
|
if err := deployCmd.Flags.Parse(args); err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("[部署成功] 目标环境:%s,全局配置文件:%s\n",
|
|
deployEnv, global.Config)
|
|
return nil
|
|
}
|
|
|
|
// 注册子命令
|
|
commands = map[string]*SubCommand{
|
|
"build": buildCmd,
|
|
"deploy": deployCmd,
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
// 1. 初始化全局参数 FlagSet(独立隔离)
|
|
var global GlobalOptions
|
|
globalFlags := flag.NewFlagSet("global", flag.ContinueOnError)
|
|
globalFlags.BoolVar(&global.Verbose, "verbose", false, "启用详细日志输出")
|
|
globalFlags.StringVar(&global.Config, "config", "./config.yaml", "全局配置文件路径")
|
|
|
|
// 2. 解析全局参数
|
|
// 注意:flag 库遇到第一个非 Flag 参数(即子命令名称)就会停止解析
|
|
if err := globalFlags.Parse(os.Args[1:]); err != nil {
|
|
if err == flag.ErrHelp {
|
|
printHelp(globalFlags)
|
|
return
|
|
}
|
|
fmt.Println("全局参数解析失败:", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 3. 获取剩余参数(子命令 + 子命令参数)
|
|
remainingArgs := globalFlags.Args()
|
|
if len(remainingArgs) == 0 {
|
|
printHelp(globalFlags)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 4. 路由分发执行子命令
|
|
cmdName := remainingArgs[0]
|
|
if cmd, exists := commands[cmdName]; exists {
|
|
if err := cmd.Run(remainingArgs[1:], global); err != nil {
|
|
if err == flag.ErrHelp {
|
|
// 当子命令触发 -h 时,打印该子命令专属的帮助信息
|
|
fmt.Printf("\n===== 子命令 [%s] 帮助信息 =====\n", cmd.Name)
|
|
fmt.Printf("说明:%s\n\n用法:%s [选项]\n\n选项:\n", cmd.Description, cmd.Name)
|
|
cmd.Flags.PrintDefaults()
|
|
return
|
|
}
|
|
fmt.Printf("子命令执行失败:%v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
} else {
|
|
fmt.Printf("❌ 未知子命令:%s\n", cmdName)
|
|
printHelp(globalFlags)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// printHelp 统一打印全局帮助信息
|
|
func printHelp(globalFlags *flag.FlagSet) {
|
|
fmt.Println("===== 项目工程化 CLI 工具 =====")
|
|
fmt.Println("用法:tool [全局选项] <子命令> [子命令选项]")
|
|
fmt.Println("\n全局选项:")
|
|
globalFlags.PrintDefaults()
|
|
|
|
fmt.Println("\n可用子命令:")
|
|
for _, cmd := range commands {
|
|
fmt.Printf(" %-10s %s\n", cmd.Name, cmd.Description)
|
|
}
|
|
fmt.Println("\n提示:使用 'tool <子命令> -h' 查看子命令详细帮助")
|
|
}
|