命令行应用
关于 Hiboot 命令行应用
Hiboot 命令行应用基于 Cobra 构建,并集成了 Hiboot 的依赖注入和自动配置功能。
功能特性
- 命令的依赖注入
- 支持层级结构的子命令
- 标志解析和验证
- 属性值注入
- 自动配置支持
项目结构
.
├── cmd
│ ├── root.go
│ ├── root_test.go
│ ├── sub.go
│ └── sub_test.go
├── config
│ └── application.yml
├── main.go
└── main_test.go
基础示例
main.go
package main
import (
"github.com/hidevopsio/hiboot/pkg/app"
"github.com/hidevopsio/hiboot/pkg/app/cli"
)
func main() {
cli.NewApplication(NewRootCommand).
SetProperty(app.BannerDisabled, true).
Run()
}
根命令
每个命令行应用都有一个根命令。在结构体中嵌入 cli.RootCommand 来标记它为根命令:
package main
import (
"fmt"
"github.com/hidevopsio/hiboot/pkg/app/cli"
)
// RootCommand 是根命令
type RootCommand struct {
cli.RootCommand
name string
verbose bool
}
// NewRootCommand 创建根命令
func NewRootCommand() *RootCommand {
c := new(RootCommand)
c.Use = "myapp"
c.Short = "我的命令行应用"
c.Long = "使用 Hiboot 构建的命令行应用"
c.Example = `
myapp -n 张三
myapp --name 张三 --verbose
`
// 定义标志
c.PersistentFlags().StringVarP(&c.name, "name", "n", "World", "要问候的名字")
c.PersistentFlags().BoolVarP(&c.verbose, "verbose", "v", false, "启用详细输出")
return c
}
// Run 执行命令
func (c *RootCommand) Run(args []string) error {
if c.verbose {
fmt.Printf("已启用详细模式\n")
}
fmt.Printf("你好,%s!\n", c.name)
return nil
}
带子命令的高级示例
main.go
package main
import (
"github.com/hidevopsio/hiboot/pkg/app"
"github.com/hidevopsio/hiboot/pkg/app/cli"
"github.com/hidevopsio/hiboot/pkg/starter/logging"
"myapp/cmd"
)
func main() {
cli.NewApplication(cmd.NewRootCommand).
SetProperty(app.BannerDisabled, true).
SetProperty(app.ProfilesInclude, logging.Profile).
Run()
}
cmd/root.go
package cmd
import (
"github.com/hidevopsio/hiboot/pkg/app/cli"
"github.com/hidevopsio/hiboot/pkg/log"
)
// RootCommand 是根命令
type RootCommand struct {
cli.RootCommand
profile string
timeout int
// 属性注入
AppName string `value:"${app.name}"`
}
// NewRootCommand 创建根命令,子命令通过依赖注入传入
func NewRootCommand(userCmd *userCommand, configCmd *configCommand) *RootCommand {
c := new(RootCommand)
c.Use = "myapp"
c.Short = "我的命令行应用"
c.Long = "带子命令的命令行应用"
pf := c.PersistentFlags()
pf.StringVarP(&c.profile, "profile", "p", "dev", "环境配置")
pf.IntVarP(&c.timeout, "timeout", "t", 30, "超时时间(秒)")
// 添加子命令
c.Add(userCmd, configCmd)
return c
}
// Run 在没有指定子命令时执行
func (c *RootCommand) Run(args []string) error {
log.Infof("应用: %s, 配置: %s, 超时: %d", c.AppName, c.profile, c.timeout)
return nil
}
cmd/user.go(子命令)
package cmd
import (
"github.com/hidevopsio/hiboot/pkg/app"
"github.com/hidevopsio/hiboot/pkg/app/cli"
"github.com/hidevopsio/hiboot/pkg/log"
)
type userCommand struct {
cli.SubCommand
}
func init() {
app.Register(newUserCommand)
}
func newUserCommand(listCmd *listCommand, addCmd *addCommand) *userCommand {
c := new(userCommand)
c.Use = "user"
c.Short = "用户管理"
c.Long = "管理应用中的用户"
c.Add(listCmd, addCmd)
return c
}
func (c *userCommand) Run(args []string) error {
log.Info("用户命令 - 请使用子命令")
return nil
}
cmd/list.go(嵌套子命令)
package cmd
import (
"fmt"
"github.com/hidevopsio/hiboot/pkg/app"
"github.com/hidevopsio/hiboot/pkg/app/cli"
)
type listCommand struct {
cli.SubCommand
all bool
}
func init() {
app.Register(newListCommand)
}
func newListCommand() *listCommand {
c := new(listCommand)
c.Use = "list"
c.Short = "列出用户"
c.Long = "列出系统中的所有用户"
c.Flags().BoolVarP(&c.all, "all", "a", false, "显示所有用户(包括非活跃用户)")
return c
}
func (c *listCommand) Run(args []string) error {
if c.all {
fmt.Println("正在列出所有用户(包括非活跃用户)...")
} else {
fmt.Println("正在列出活跃用户...")
}
return nil
}
运行应用
# 运行根命令
go run main.go
# 带标志运行
go run main.go --profile=prod --timeout=60
# 运行子命令
go run main.go user list
# 带标志运行子命令
go run main.go user list --all
# 获取帮助
go run main.go --help
go run main.go user --help
配置
命令行应用可以像 Web 应用一样使用配置文件:
config/application.yml
app:
name: my-cli-app
logging:
level: info
可以使用 value:"" 标签将属性注入到命令中:
type RootCommand struct {
cli.RootCommand
AppName string `value:"${app.name}"`
LogLevel string `value:"${logging.level}"`
}
单元测试
package cmd
import (
"testing"
"github.com/hidevopsio/hiboot/pkg/app/cli"
"github.com/stretchr/testify/assert"
)
func TestRootCommand(t *testing.T) {
testApp := cli.NewTestApplication(t, NewRootCommand)
t.Run("应该运行根命令", func(t *testing.T) {
_, err := testApp.Run("--name", "Test")
assert.NoError(t, err)
})
t.Run("应该使用详细标志运行", func(t *testing.T) {
_, err := testApp.Run("--name", "Test", "--verbose")
assert.NoError(t, err)
})
}