diff --git a/tools/goctl/api/cmd.go b/tools/goctl/api/cmd.go index 3c550f30..08638052 100644 --- a/tools/goctl/api/cmd.go +++ b/tools/goctl/api/cmd.go @@ -13,166 +13,91 @@ import ( "github.com/zeromicro/go-zero/tools/goctl/api/tsgen" "github.com/zeromicro/go-zero/tools/goctl/api/validate" "github.com/zeromicro/go-zero/tools/goctl/config" + "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" "github.com/zeromicro/go-zero/tools/goctl/plugin" ) var ( // Cmd describes an api command. - Cmd = &cobra.Command{ - Use: "api", - Short: "Generate api related files", - RunE: apigen.CreateApiTemplate, - } - - dartCmd = &cobra.Command{ - Use: "dart", - Short: "Generate dart files for provided api in api file", - RunE: dartgen.DartCommand, - } - - docCmd = &cobra.Command{ - Use: "doc", - Short: "Generate doc files", - RunE: docgen.DocCommand, - } - - formatCmd = &cobra.Command{ - Use: "format", - Short: "Format api files", - RunE: format.GoFormatApi, - } - - goCmd = &cobra.Command{ - Use: "go", - Short: "Generate go files for provided api in api file", - RunE: gogen.GoCommand, - } - - newCmd = &cobra.Command{ - Use: "new", - Short: "Fast create api service", - Example: "goctl api new [options] service-name", - Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - RunE: func(cmd *cobra.Command, args []string) error { - return new.CreateServiceCommand(args) - }, - } - - validateCmd = &cobra.Command{ - Use: "validate", - Short: "Validate api file", - RunE: validate.GoValidateApi, - } - - javaCmd = &cobra.Command{ - Use: "java", - Short: "Generate java files for provided api in api file", - Hidden: true, - RunE: javagen.JavaCommand, - } - - ktCmd = &cobra.Command{ - Use: "kt", - Short: "Generate kotlin code for provided api file", - RunE: ktgen.KtCommand, - } - - pluginCmd = &cobra.Command{ - Use: "plugin", - Short: "Custom file generator", - RunE: plugin.PluginCommand, - } - - tsCmd = &cobra.Command{ - Use: "ts", - Short: "Generate ts files for provided api in api file", - RunE: tsgen.TsCommand, - } + Cmd = cobrax.NewCommand("api", cobrax.WithRunE(apigen.CreateApiTemplate)) + dartCmd = cobrax.NewCommand("dart", cobrax.WithRunE(dartgen.DartCommand)) + docCmd = cobrax.NewCommand("doc", cobrax.WithRunE(docgen.DocCommand)) + formatCmd = cobrax.NewCommand("format", cobrax.WithRunE(format.GoFormatApi)) + goCmd = cobrax.NewCommand("go", cobrax.WithRunE(gogen.GoCommand)) + newCmd = cobrax.NewCommand("new", cobrax.WithRunE(new.CreateServiceCommand), + cobrax.WithArgs(cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs))) + validateCmd = cobrax.NewCommand("validate", cobrax.WithRunE(validate.GoValidateApi)) + javaCmd = cobrax.NewCommand("java", cobrax.WithRunE(javagen.JavaCommand), cobrax.WithHidden()) + ktCmd = cobrax.NewCommand("kt", cobrax.WithRunE(ktgen.KtCommand)) + pluginCmd = cobrax.NewCommand("plugin", cobrax.WithRunE(plugin.PluginCommand)) + tsCmd = cobrax.NewCommand("ts", cobrax.WithRunE(tsgen.TsCommand)) ) func init() { - Cmd.Flags().StringVar(&apigen.VarStringOutput, "o", "", "Output a sample api file") - Cmd.Flags().StringVar(&apigen.VarStringHome, "home", "", "The goctl home path of the"+ - " template, --home and --remote cannot be set at the same time, if they are, --remote has "+ - "higher priority") - Cmd.Flags().StringVar(&apigen.VarStringRemote, "remote", "", "The remote git repo of the"+ - " template, --home and --remote cannot be set at the same time, if they are, --remote has higher"+ - " priority\nThe git repo directory must be consistent with the"+ - " https://github.com/zeromicro/go-zero-template directory structure") - Cmd.Flags().StringVar(&apigen.VarStringBranch, "branch", "", "The branch of the "+ - "remote repo, it does work with --remote") + var ( + apiCmdFlags = Cmd.Flags() + dartCmdFlags = dartCmd.Flags() + docCmdFlags = docCmd.Flags() + formatCmdFlags = formatCmd.Flags() + goCmdFlags = goCmd.Flags() + javaCmdFlags = javaCmd.Flags() + ktCmdFlags = ktCmd.Flags() + newCmdFlags = newCmd.Flags() + pluginCmdFlags = pluginCmd.Flags() + tsCmdFlags = tsCmd.Flags() + validateCmdFlags = validateCmd.Flags() + ) - dartCmd.Flags().StringVar(&dartgen.VarStringDir, "dir", "", "The target dir") - dartCmd.Flags().StringVar(&dartgen.VarStringAPI, "api", "", "The api file") - dartCmd.Flags().BoolVar(&dartgen.VarStringLegacy, "legacy", false, "Legacy generator for flutter v1") - dartCmd.Flags().StringVar(&dartgen.VarStringHostname, "hostname", "", "hostname of the server") - dartCmd.Flags().StringVar(&dartgen.VarStringScheme, "scheme", "", "scheme of the server") + apiCmdFlags.StringVar(&apigen.VarStringOutput, "o") + apiCmdFlags.StringVar(&apigen.VarStringHome, "home") + apiCmdFlags.StringVar(&apigen.VarStringRemote, "remote") + apiCmdFlags.StringVar(&apigen.VarStringBranch, "branch") - docCmd.Flags().StringVar(&docgen.VarStringDir, "dir", "", "The target dir") - docCmd.Flags().StringVar(&docgen.VarStringOutput, "o", "", "The output markdown directory") + dartCmdFlags.StringVar(&dartgen.VarStringDir, "dir") + dartCmdFlags.StringVar(&dartgen.VarStringAPI, "api") + dartCmdFlags.BoolVar(&dartgen.VarStringLegacy, "legacy") + dartCmdFlags.StringVar(&dartgen.VarStringHostname, "hostname") + dartCmdFlags.StringVar(&dartgen.VarStringScheme, "scheme") - formatCmd.Flags().StringVar(&format.VarStringDir, "dir", "", "The format target dir") - formatCmd.Flags().BoolVar(&format.VarBoolIgnore, "iu", false, "Ignore update") - formatCmd.Flags().BoolVar(&format.VarBoolUseStdin, "stdin", false, "Use stdin to input api"+ - " doc content, press \"ctrl + d\" to send EOF") - formatCmd.Flags().BoolVar(&format.VarBoolSkipCheckDeclare, "declare", false, "Use to skip check "+ - "api types already declare") + docCmdFlags.StringVar(&docgen.VarStringDir, "dir") + docCmdFlags.StringVar(&docgen.VarStringOutput, "o") - goCmd.Flags().StringVar(&gogen.VarStringDir, "dir", "", "The target dir") - goCmd.Flags().StringVar(&gogen.VarStringAPI, "api", "", "The api file") - goCmd.Flags().StringVar(&gogen.VarStringHome, "home", "", "The goctl home path of "+ - "the template, --home and --remote cannot be set at the same time, if they are, --remote "+ - "has higher priority") - goCmd.Flags().StringVar(&gogen.VarStringRemote, "remote", "", "The remote git repo "+ - "of the template, --home and --remote cannot be set at the same time, if they are, --remote"+ - " has higher priority\nThe git repo directory must be consistent with the "+ - "https://github.com/zeromicro/go-zero-template directory structure") - goCmd.Flags().StringVar(&gogen.VarStringBranch, "branch", "", "The branch of "+ - "the remote repo, it does work with --remote") - goCmd.Flags().StringVar(&gogen.VarStringStyle, "style", config.DefaultFormat, "The file naming format,"+ - " see [https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md]") + formatCmdFlags.StringVar(&format.VarStringDir, "dir") + formatCmdFlags.BoolVar(&format.VarBoolIgnore, "iu") + formatCmdFlags.BoolVar(&format.VarBoolUseStdin, "stdin") + formatCmdFlags.BoolVar(&format.VarBoolSkipCheckDeclare, "declare") - javaCmd.Flags().StringVar(&javagen.VarStringDir, "dir", "", "The target dir") - javaCmd.Flags().StringVar(&javagen.VarStringAPI, "api", "", "The api file") + goCmdFlags.StringVar(&gogen.VarStringDir, "dir") + goCmdFlags.StringVar(&gogen.VarStringAPI, "api") + goCmdFlags.StringVar(&gogen.VarStringHome, "home") + goCmdFlags.StringVar(&gogen.VarStringRemote, "remote") + goCmdFlags.StringVar(&gogen.VarStringBranch, "branch") + goCmdFlags.StringVarWithDefaultValue(&gogen.VarStringStyle, "style", config.DefaultFormat) - ktCmd.Flags().StringVar(&ktgen.VarStringDir, "dir", "", "The target dir") - ktCmd.Flags().StringVar(&ktgen.VarStringAPI, "api", "", "The api file") - ktCmd.Flags().StringVar(&ktgen.VarStringPKG, "pkg", "", "Define package name for kotlin file") + javaCmdFlags.StringVar(&javagen.VarStringDir, "dir") + javaCmdFlags.StringVar(&javagen.VarStringAPI, "api") - newCmd.Flags().StringVar(&new.VarStringHome, "home", "", "The goctl home path of "+ - "the template, --home and --remote cannot be set at the same time, if they are, --remote "+ - "has higher priority") - newCmd.Flags().StringVar(&new.VarStringRemote, "remote", "", "The remote git repo "+ - "of the template, --home and --remote cannot be set at the same time, if they are, --remote"+ - " has higher priority\n\tThe git repo directory must be consistent with the "+ - "https://github.com/zeromicro/go-zero-template directory structure") - newCmd.Flags().StringVar(&new.VarStringBranch, "branch", "", "The branch of "+ - "the remote repo, it does work with --remote") - newCmd.Flags().StringVar(&new.VarStringStyle, "style", config.DefaultFormat, "The file naming format,"+ - " see [https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md]") + ktCmdFlags.StringVar(&ktgen.VarStringDir, "dir") + ktCmdFlags.StringVar(&ktgen.VarStringAPI, "api") + ktCmdFlags.StringVar(&ktgen.VarStringPKG, "pkg") - pluginCmd.Flags().StringVarP(&plugin.VarStringPlugin, "plugin", "p", "", "The plugin file") - pluginCmd.Flags().StringVar(&plugin.VarStringDir, "dir", "", "The target dir") - pluginCmd.Flags().StringVar(&plugin.VarStringAPI, "api", "", "The api file") - pluginCmd.Flags().StringVar(&plugin.VarStringStyle, "style", "", - "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]") + newCmdFlags.StringVar(&new.VarStringHome, "home") + newCmdFlags.StringVar(&new.VarStringRemote, "remote") + newCmdFlags.StringVar(&new.VarStringBranch, "branch") + newCmdFlags.StringVarWithDefaultValue(&new.VarStringStyle, "style", config.DefaultFormat) - tsCmd.Flags().StringVar(&tsgen.VarStringDir, "dir", "", "The target dir") - tsCmd.Flags().StringVar(&tsgen.VarStringAPI, "api", "", "The api file") - tsCmd.Flags().StringVar(&tsgen.VarStringCaller, "caller", "", "The web api caller") - tsCmd.Flags().BoolVar(&tsgen.VarBoolUnWrap, "unwrap", false, "Unwrap the webapi caller for import") + pluginCmdFlags.StringVarP(&plugin.VarStringPlugin, "plugin", "p") + pluginCmdFlags.StringVar(&plugin.VarStringDir, "dir") + pluginCmdFlags.StringVar(&plugin.VarStringAPI, "api") + pluginCmdFlags.StringVar(&plugin.VarStringStyle, "style") - validateCmd.Flags().StringVar(&validate.VarStringAPI, "api", "", "Validate target api file") + tsCmdFlags.StringVar(&tsgen.VarStringDir, "dir") + tsCmdFlags.StringVar(&tsgen.VarStringAPI, "api") + tsCmdFlags.StringVar(&tsgen.VarStringCaller, "caller") + tsCmdFlags.BoolVar(&tsgen.VarBoolUnWrap, "unwrap") + + validateCmdFlags.StringVar(&validate.VarStringAPI, "api") // Add sub-commands - Cmd.AddCommand(dartCmd) - Cmd.AddCommand(docCmd) - Cmd.AddCommand(formatCmd) - Cmd.AddCommand(goCmd) - Cmd.AddCommand(javaCmd) - Cmd.AddCommand(ktCmd) - Cmd.AddCommand(newCmd) - Cmd.AddCommand(pluginCmd) - Cmd.AddCommand(tsCmd) - Cmd.AddCommand(validateCmd) + Cmd.AddCommand(dartCmd, docCmd, formatCmd, goCmd, javaCmd, ktCmd, newCmd, pluginCmd, tsCmd, validateCmd) } diff --git a/tools/goctl/api/new/newservice.go b/tools/goctl/api/new/newservice.go index 707fe3d1..9241d855 100644 --- a/tools/goctl/api/new/newservice.go +++ b/tools/goctl/api/new/newservice.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" + "github.com/spf13/cobra" "github.com/zeromicro/go-zero/tools/goctl/api/gogen" conf "github.com/zeromicro/go-zero/tools/goctl/config" "github.com/zeromicro/go-zero/tools/goctl/util" @@ -29,7 +30,7 @@ var ( ) // CreateServiceCommand fast create service -func CreateServiceCommand(args []string) error { +func CreateServiceCommand(_ *cobra.Command, args []string) error { dirName := args[0] if len(VarStringStyle) == 0 { VarStringStyle = conf.DefaultFormat diff --git a/tools/goctl/bug/cmd.go b/tools/goctl/bug/cmd.go index c259991b..b78de540 100644 --- a/tools/goctl/bug/cmd.go +++ b/tools/goctl/bug/cmd.go @@ -1,11 +1,9 @@ package bug -import "github.com/spf13/cobra" +import ( + "github.com/spf13/cobra" + "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" +) // Cmd describes a bug command. -var Cmd = &cobra.Command{ - Use: "bug", - Short: "Report a bug", - Args: cobra.NoArgs, - RunE: runE, -} +var Cmd = cobrax.NewCommand("bug", cobrax.WithRunE(cobra.NoArgs), cobrax.WithArgs(cobra.NoArgs)) diff --git a/tools/goctl/cmd/root.go b/tools/goctl/cmd/root.go index 6aa488e6..870ecd1b 100644 --- a/tools/goctl/cmd/root.go +++ b/tools/goctl/cmd/root.go @@ -15,6 +15,8 @@ import ( "github.com/zeromicro/go-zero/tools/goctl/bug" "github.com/zeromicro/go-zero/tools/goctl/docker" "github.com/zeromicro/go-zero/tools/goctl/env" + "github.com/zeromicro/go-zero/tools/goctl/gateway" + "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" "github.com/zeromicro/go-zero/tools/goctl/internal/version" "github.com/zeromicro/go-zero/tools/goctl/kube" "github.com/zeromicro/go-zero/tools/goctl/migrate" @@ -35,14 +37,7 @@ const ( var ( //go:embed usage.tpl usageTpl string - - rootCmd = &cobra.Command{ - Use: "goctl", - Short: "A cli tool to generate go-zero code", - Long: "A cli tool to generate api, zrpc, model code\n\n" + - "GitHub: https://github.com/zeromicro/go-zero\n" + - "Site: https://go-zero.dev", - } + rootCmd = cobrax.NewCommand("goctl") ) // Execute executes the given command @@ -117,16 +112,8 @@ func init() { runtime.GOOS, runtime.GOARCH) rootCmd.SetUsageTemplate(usageTpl) - rootCmd.AddCommand(api.Cmd) - rootCmd.AddCommand(bug.Cmd) - rootCmd.AddCommand(docker.Cmd) - rootCmd.AddCommand(kube.Cmd) - rootCmd.AddCommand(env.Cmd) - rootCmd.AddCommand(model.Cmd) - rootCmd.AddCommand(migrate.Cmd) - rootCmd.AddCommand(quickstart.Cmd) - rootCmd.AddCommand(rpc.Cmd) - rootCmd.AddCommand(tpl.Cmd) - rootCmd.AddCommand(upgrade.Cmd) - rootCmd.AddCommand(cobracompletefig.CreateCompletionSpecCommand()) + rootCmd.AddCommand(api.Cmd, bug.Cmd, docker.Cmd, kube.Cmd, env.Cmd, gateway.Cmd, model.Cmd) + rootCmd.AddCommand(migrate.Cmd, quickstart.Cmd, rpc.Cmd, tpl.Cmd, upgrade.Cmd) + rootCmd.Command.AddCommand(cobracompletefig.CreateCompletionSpecCommand()) + rootCmd.MustInit() } diff --git a/tools/goctl/docker/cmd.go b/tools/goctl/docker/cmd.go index 325382e4..d0a3bab0 100644 --- a/tools/goctl/docker/cmd.go +++ b/tools/goctl/docker/cmd.go @@ -1,6 +1,6 @@ package docker -import "github.com/spf13/cobra" +import "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" var ( varExeName string @@ -14,21 +14,18 @@ var ( varStringTZ string // Cmd describes a docker command. - Cmd = &cobra.Command{ - Use: "docker", - Short: "Generate Dockerfile", - RunE: dockerCommand, - } + Cmd = cobrax.NewCommand("docker", cobrax.WithRunE(dockerCommand)) ) func init() { - Cmd.Flags().StringVar(&varExeName, "exe", "", "The executable name in the built image") - Cmd.Flags().StringVar(&varStringGo, "go", "", "The file that contains main function") - Cmd.Flags().StringVar(&varStringBase, "base", "scratch", "The base image to build the docker image, default scratch") - Cmd.Flags().IntVar(&varIntPort, "port", 0, "The port to expose, default none") - Cmd.Flags().StringVar(&varStringHome, "home", "", "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority") - Cmd.Flags().StringVar(&varStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure") - Cmd.Flags().StringVar(&varStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote") - Cmd.Flags().StringVar(&varStringVersion, "version", "", "The goctl builder golang image version") - Cmd.Flags().StringVar(&varStringTZ, "tz", "Asia/Shanghai", "The timezone of the container") + dockerCmdFlags := Cmd.Flags() + dockerCmdFlags.StringVar(&varExeName, "exe") + dockerCmdFlags.StringVar(&varStringGo, "go") + dockerCmdFlags.StringVarWithDefaultValue(&varStringBase, "base", "scratch") + dockerCmdFlags.IntVar(&varIntPort, "port") + dockerCmdFlags.StringVar(&varStringHome, "home") + dockerCmdFlags.StringVar(&varStringRemote, "remote") + dockerCmdFlags.StringVar(&varStringBranch, "branch") + dockerCmdFlags.StringVar(&varStringVersion, "version") + dockerCmdFlags.StringVarWithDefaultValue(&varStringTZ, "tz", "Asia/Shanghai") } diff --git a/tools/goctl/env/cmd.go b/tools/goctl/env/cmd.go index 58b7e112..84e37fd8 100644 --- a/tools/goctl/env/cmd.go +++ b/tools/goctl/env/cmd.go @@ -1,6 +1,6 @@ package env -import "github.com/spf13/cobra" +import "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" var ( sliceVarWriteValue []string @@ -9,38 +9,20 @@ var ( boolVarInstall bool // Cmd describes an env command. - Cmd = &cobra.Command{ - Use: "env", - Short: "Check or edit goctl environment", - RunE: write, - } - installCmd = &cobra.Command{ - Use: "install", - Short: "Goctl env installation", - RunE: install, - } - checkCmd = &cobra.Command{ - Use: "check", - Short: "Detect goctl env and dependency tools", - RunE: check, - } + Cmd = cobrax.NewCommand("env", cobrax.WithRunE(write)) + installCmd = cobrax.NewCommand("install", cobrax.WithRunE(install)) + checkCmd = cobrax.NewCommand("check", cobrax.WithRunE(check)) ) func init() { // The root command flags - Cmd.Flags().StringSliceVarP(&sliceVarWriteValue, - "write", "w", nil, "Edit goctl environment") - Cmd.PersistentFlags().BoolVarP(&boolVarForce, - "force", "f", false, - "Silent installation of non-existent dependencies") - Cmd.PersistentFlags().BoolVarP(&boolVarVerbose, - "verbose", "v", false, "Enable log output") + Cmd.Flags().StringSliceVarP(&sliceVarWriteValue, "write", "w") + Cmd.PersistentFlags().BoolVarP(&boolVarForce, "force", "f") + Cmd.PersistentFlags().BoolVarP(&boolVarVerbose, "verbose", "v") // The sub-command flags - checkCmd.Flags().BoolVarP(&boolVarInstall, "install", "i", - false, "Install dependencies if not found") + checkCmd.Flags().BoolVarP(&boolVarInstall, "install", "i") // Add sub-command - Cmd.AddCommand(installCmd) - Cmd.AddCommand(checkCmd) + Cmd.AddCommand(checkCmd, installCmd) } diff --git a/tools/goctl/env/env.go b/tools/goctl/env/env.go index a4cfae1f..f0891374 100644 --- a/tools/goctl/env/env.go +++ b/tools/goctl/env/env.go @@ -7,10 +7,10 @@ import ( "github.com/zeromicro/go-zero/tools/goctl/pkg/env" ) -func write(_ *cobra.Command, _ []string) error { +func write(_ *cobra.Command, args []string) error { if len(sliceVarWriteValue) > 0 { return env.WriteEnv(sliceVarWriteValue) } - fmt.Println(env.Print()) + fmt.Println(env.Print(args...)) return nil } diff --git a/tools/goctl/env/install.go b/tools/goctl/env/install.go index 0768a868..f316a856 100644 --- a/tools/goctl/env/install.go +++ b/tools/goctl/env/install.go @@ -1,8 +1,6 @@ package env -import ( - "github.com/spf13/cobra" -) +import "github.com/spf13/cobra" func install(_ *cobra.Command, _ []string) error { return Prepare(true, boolVarForce, boolVarVerbose) diff --git a/tools/goctl/gateway/cmd.go b/tools/goctl/gateway/cmd.go new file mode 100644 index 00000000..44b0d09f --- /dev/null +++ b/tools/goctl/gateway/cmd.go @@ -0,0 +1,60 @@ +package gateway + +import ( + _ "embed" + "os" + "path/filepath" + + "github.com/spf13/cobra" + "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" + "github.com/zeromicro/go-zero/tools/goctl/util/ctx" + "github.com/zeromicro/go-zero/tools/goctl/util/pathx" +) + +var ( + varStringHome string + varStringRemote string + varStringBranch string + varStringDir string + + Cmd = cobrax.NewCommand("gateway", cobrax.WithRunE(generateGateway)) +) + +func init() { + Cmd.PersistentFlags().StringVar(&varStringHome, "home") + Cmd.PersistentFlags().StringVar(&varStringRemote, "remote") + Cmd.PersistentFlags().StringVar(&varStringBranch, "branch") + Cmd.PersistentFlags().StringVar(&varStringDir, "dir") +} + +func generateGateway(*cobra.Command, []string) error { + if err:=pathx.MkdirIfNotExist(varStringDir);err!=nil{ + return err + } + + if _,err:=ctx.Prepare(varStringDir);err!=nil{ + return err + } + + etcContent, err := pathx.LoadTemplate(category, etcTemplateFileFile, etcTemplate) + if err != nil { + return err + } + + mainContent, err := pathx.LoadTemplate(category, mainTemplateFile, mainTemplate) + if err != nil { + return err + } + + etcDir := filepath.Join(varStringDir, "etc") + if err := pathx.MkdirIfNotExist(etcDir); err != nil { + return err + } + etcFile := filepath.Join(etcDir, "gateway.yaml") + if err := os.WriteFile(etcFile, []byte(etcContent), 0644); err != nil { + return err + } + + mainFile := filepath.Join(varStringDir, "main.go") + return os.WriteFile(mainFile, []byte(mainContent), 0644) +} diff --git a/tools/goctl/gateway/conf.yml b/tools/goctl/gateway/conf.yml new file mode 100644 index 00000000..0a70f1ac --- /dev/null +++ b/tools/goctl/gateway/conf.yml @@ -0,0 +1,18 @@ +Name: gateway-example # gateway name +Host: localhost # gateway host +Port: 8888 # gateway port +Upstreams: # upstreams + - Grpc: # grpc upstream + Target: 0.0.0.0:8080 # grpc target,the direct grpc server address,for only one node +# Endpoints: [0.0.0.0:8080,192.168.120.1:8080] # grpc endpoints, the grpc server address list, for multiple nodes +# Etcd: # etcd config, if you want to use etcd to discover the grpc server address +# Hosts: [127.0.0.1:2378,127.0.0.1:2379] # etcd hosts +# Key: greet.grpc # the discovery key + # protoset mode + ProtoSets: + - hello.pb + # Mappings can also be written in proto options +# Mappings: # routes mapping +# - Method: get +# Path: /ping +# RpcPath: hello.Hello/Ping diff --git a/tools/goctl/gateway/gateway.tpl b/tools/goctl/gateway/gateway.tpl new file mode 100644 index 00000000..62734516 --- /dev/null +++ b/tools/goctl/gateway/gateway.tpl @@ -0,0 +1,20 @@ +package main + +import ( + "flag" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/gateway" +) + +var configFile = flag.String("f", "etc/gateway.yaml", "config file") + +func main() { + flag.Parse() + + var c gateway.GatewayConf + conf.MustLoad(*configFile, &c) + gw := gateway.MustNewServer(c) + defer gw.Stop() + gw.Start() +} diff --git a/tools/goctl/gateway/template.go b/tools/goctl/gateway/template.go new file mode 100644 index 00000000..64ebd7ab --- /dev/null +++ b/tools/goctl/gateway/template.go @@ -0,0 +1,62 @@ +package gateway + +import ( + _ "embed" + "fmt" + + "github.com/zeromicro/go-zero/tools/goctl/util/pathx" +) + +const ( + category = "gateway" + etcTemplateFileFile = "etc.tpl" + mainTemplateFile = "main.tpl" +) + +//go:embed conf.yml +var etcTemplate string + +//go:embed gateway.tpl +var mainTemplate string + + +var templates = map[string]string{ + etcTemplateFileFile: etcTemplate, + mainTemplateFile: mainTemplate, +} + +// GenTemplates is the entry for command goctl template, +// it will create the specified category +func GenTemplates() error { + return pathx.InitTemplates(category, templates) +} + +// RevertTemplate restores the deleted template files +func RevertTemplate(name string) error { + content, ok := templates[name] + if !ok { + return fmt.Errorf("%s: no such file name", name) + } + return pathx.CreateTemplate(category, name, content) +} + +// Clean deletes all template files +func Clean() error { + return pathx.Clean(category) +} + +// Update is used to update the template files, it will delete the existing old templates at first, +// and then create the latest template files +func Update() error { + err := Clean() + if err != nil { + return err + } + + return pathx.InitTemplates(category, templates) +} + +// Category returns a const string value for rpc template category +func Category() string { + return category +} diff --git a/tools/goctl/go.mod b/tools/goctl/go.mod index 7d854694..862420a1 100644 --- a/tools/goctl/go.mod +++ b/tools/goctl/go.mod @@ -10,6 +10,7 @@ require ( github.com/iancoleman/strcase v0.2.0 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/spf13/cobra v1.6.1 + github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.2 github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 github.com/zeromicro/antlr v0.0.1 @@ -34,6 +35,7 @@ require ( github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/fatih/color v1.14.1 // indirect github.com/felixge/fgprof v0.9.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect @@ -69,7 +71,6 @@ require ( github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 // indirect go.etcd.io/etcd/api/v3 v3.5.7 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect diff --git a/tools/goctl/go.sum b/tools/goctl/go.sum index e231219d..9343a8f5 100644 --- a/tools/goctl/go.sum +++ b/tools/goctl/go.sum @@ -101,7 +101,8 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -541,6 +542,7 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/tools/goctl/internal/cobrax/cobrax.go b/tools/goctl/internal/cobrax/cobrax.go new file mode 100644 index 00000000..d9af632e --- /dev/null +++ b/tools/goctl/internal/cobrax/cobrax.go @@ -0,0 +1,171 @@ +package cobrax + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/zeromicro/go-zero/tools/goctl/internal/flags" +) + +type Option func(*cobra.Command) + +func WithRunE(runE func(*cobra.Command, []string) error) Option { + return func(cmd *cobra.Command) { + cmd.RunE = runE + } +} + +func WithRun(run func(*cobra.Command, []string)) Option { + return func(cmd *cobra.Command) { + cmd.Run = run + } +} + +func WithArgs(arg cobra.PositionalArgs) Option { + return func(command *cobra.Command) { + command.Args = arg + } +} + +func WithHidden() Option { + return func(command *cobra.Command) { + command.Hidden = true + } +} + +type Command struct { + *cobra.Command +} + +type FlagSet struct { + *pflag.FlagSet +} + +func (f *FlagSet) StringVar(p *string, name string) { + f.StringVarWithDefaultValue(p, name, "") +} + +func (f *FlagSet) StringVarWithDefaultValue(p *string, name string, value string) { + f.FlagSet.StringVar(p, name, value, "") +} + +func (f *FlagSet) StringVarP(p *string, name, shorthand string) { + f.StringVarPWithDefaultValue(p, name, shorthand, "") +} + +func (f *FlagSet) StringVarPWithDefaultValue(p *string, name, shorthand string, value string) { + f.FlagSet.StringVarP(p, name, shorthand, value, "") +} + +func (f *FlagSet) BoolVar(p *bool, name string) { + f.BoolVarWithDefaultValue(p, name, false) +} + +func (f *FlagSet) BoolVarWithDefaultValue(p *bool, name string, value bool) { + f.FlagSet.BoolVar(p, name, value, "") +} + +func (f *FlagSet) BoolVarP(p *bool, name, shorthand string) { + f.BoolVarPWithDefaultValue(p, name, shorthand, false) +} + +func (f *FlagSet) BoolVarPWithDefaultValue(p *bool, name, shorthand string, value bool) { + f.FlagSet.BoolVarP(p, name, shorthand, value, "") +} + +func (f *FlagSet) IntVar(p *int, name string) { + f.IntVarWithDefaultValue(p, name, 0) +} + +func (f *FlagSet) IntVarWithDefaultValue(p *int, name string, value int) { + f.FlagSet.IntVar(p, name, value, "") +} + +func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string) { + f.FlagSet.StringSliceVarP(p, name, shorthand, []string{}, "") +} + +func (f *FlagSet) StringSliceVarPWithDefaultValue(p *[]string, name, shorthand string, value []string) { + f.FlagSet.StringSliceVarP(p, name, shorthand, value, "") +} + +func (f *FlagSet) StringSliceVar(p *[]string, name string) { + f.StringSliceVarWithDefaultValue(p, name, []string{}) +} + +func (f *FlagSet) StringSliceVarWithDefaultValue(p *[]string, name string, value []string) { + f.FlagSet.StringSliceVar(p, name, value, "") +} + +func NewCommand(use string, opts ...Option) *Command { + c := &Command{ + Command: &cobra.Command{ + Use: use, + }, + } + + for _, opt := range opts { + opt(c.Command) + } + + return c +} + +func (c *Command) AddCommand(cmds ...*Command) { + for _, cmd := range cmds { + c.Command.AddCommand(cmd.Command) + } +} + +func (c *Command) Flags() *FlagSet { + set := c.Command.Flags() + return &FlagSet{ + FlagSet: set, + } +} + +func (c *Command) PersistentFlags() *FlagSet { + set := c.Command.PersistentFlags() + return &FlagSet{ + FlagSet: set, + } +} + +func (c *Command) MustInit() { + commands := append([]*cobra.Command{c.Command}, getCommandsRecursively(c.Command)...) + for _, command := range commands { + commandKey := getCommandName(command) + if len(command.Short) == 0 { + command.Short = flags.Get(commandKey + ".short") + } + if len(command.Long) == 0 { + command.Long = flags.Get(commandKey + ".long") + } + if len(command.Example) == 0 { + command.Example = flags.Get(commandKey + ".example") + } + command.Flags().VisitAll(func(flag *pflag.Flag) { + flag.Usage = flags.Get(fmt.Sprintf("%s.%s", commandKey, flag.Name)) + }) + command.PersistentFlags().VisitAll(func(flag *pflag.Flag) { + flag.Usage = flags.Get(fmt.Sprintf("%s.%s", commandKey, flag.Name)) + }) + } +} + +func getCommandName(cmd *cobra.Command) string { + if cmd.HasParent() { + return getCommandName(cmd.Parent()) + "." + cmd.Name() + } + return cmd.Name() +} + +func getCommandsRecursively(parent *cobra.Command) []*cobra.Command { + var commands []*cobra.Command + for _, cmd := range parent.Commands() { + commands = append(commands, cmd) + commands = append(commands, getCommandsRecursively(cmd)...) + } + return commands +} diff --git a/tools/goctl/internal/flags/default_en.json b/tools/goctl/internal/flags/default_en.json new file mode 100644 index 00000000..eaf4bafd --- /dev/null +++ b/tools/goctl/internal/flags/default_en.json @@ -0,0 +1,283 @@ +{ + "goctl": { + "short": "A cli tool to generate go-zero code", + "long": "A cli tool to generate api, zrpc, model code\n\nGitHub: https://github.com/zeromicro/go-zero\nSite: https://go-zero.dev", + "api": { + "short": "Generate api related files", + "o": "Output a sample api file", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "api": "The api file", + "dir": "The target dir", + "dart": { + "short": "Generate dart files for provided api in api file", + "dir": "{{.goctl.api.dir}}", + "api": "{{.goctl.api.api}}", + "legacy": "Legacy generator for flutter v1", + "hostname": "hostname of the server", + "scheme": "scheme of the server" + }, + "doc": { + "short": "Generate doc files", + "dir": "{{.goctl.api.dir}}", + "o": "The output markdown directory" + }, + "format": { + "short": "Format api files", + "dir": "{{.goctl.api.dir}}", + "iu": "Ignore update", + "stdin": "Use stdin to input api doc content, press \"ctrl + d\" to send EOF", + "declare": "Use to skip check api types already declare" + }, + "go": { + "short": "Generate go files for provided api in api file", + "dir": "{{.goctl.api.dir}}", + "api": "{{.goctl.api.api}}", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "style": "{{.global.style}}" + }, + "new": { + "short": "Fast create api service", + "Example": "goctl api new [options] service-name", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "style": "{{.global.style}}" + }, + "validate": { + "short": "Validate api file", + "api": "{{.goctl.api.api}}" + }, + "kt": { + "short": "Generate kotlin code for provided api file", + "dir": "{{.goctl.api.dir}}", + "api": "{{.goctl.api.api}}", + "pkg": "Define package name for kotlin file" + }, + "plugin": { + "short": "Custom file generator", + "plugin": "The plugin file", + "dir": "{{.goctl.api.dir}}", + "api": "{{.goctl.api.api}}", + "style": "{{.global.style}}" + }, + "ts": { + "short": "Generate ts files for provided api in api file", + "dir": "{{.goctl.api.dir}}", + "api": "{{.goctl.api.api}}", + "caller": "The web api caller", + "unwrap": "Unwrap the webapi caller for import" + } + }, + "bug": { + "short": "Report a bug" + }, + "docker": { + "short": "Generate Dockerfile", + "exe": "The executable name in the built image", + "go": "The file that contains main function", + "base": "The base image to build the docker image, default scratch", + "port": "The port to expose, default none", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "version": "The goctl builder golang image version", + "tz": "The timezone of the container" + }, + "kube": { + "short": "Generate kubernetes files", + "deploy": { + "short": "Generate deployment yaml file", + "name": "The name of deployment (required)", + "namespace": "The namespace of deployment (required)", + "image": "The docker image of deployment (required)", + "secret": "The secret to image pull from registry", + "requestCpu": "The request cpu to deploy", + "requestMem": "The request memory to deploy", + "limitCpu": "The limit cpu to deploy", + "limitMem": "The limit memory to deploy", + "o": "The output yaml file (required)", + "replicas": "The number of replicas to deploy", + "revisions": "The number of revision history to limit", + "port": "The port of the deployment to listen on pod (required)", + "nodePort": "The nodePort of the deployment to expose", + "targetPort": "The targetPort of the deployment, default to port", + "minReplicas": "The min replicas to deploy", + "maxReplicas": "The max replicas to deploy", + "imagePullPolicy": "The image pull policy of the deployment, default to IfNotPresent", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "serviceAccount": "TheServiceAccount for the deployment" + } + }, + "env": { + "short": "Check or edit goctl environment", + "write": "Edit goctl environment", + "force": "Silent installation of non-existent dependencies", + "verbose": "Enable log output", + "install": { + "short": "Goctl env installation" + }, + "check": { + "short": "Detect goctl env and dependency tools", + "install": "Install dependencies if not found" + } + }, + "gateway": { + "short": "gateway is a tool to generate gateway code", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "dir": "The output dir", + "protoc": { + "short": "generate gateway code from proto file" + }, + "protoset": { + "short": "generate gateway code from protoset file" + }, + "server": { + "short": "generate gateway code from grpc server" + } + }, + "model": { + "short": "Generate model code", + "dir": "The target dir", + "mysql": { + "short": "Generate mysql model", + "strict": "Generate model in strict mode", + "ignore-columns": "Ignore columns while creating or updating rows", + "datasource": { + "short": "Generate model from datasource", + "url": "The data source of database,like \"root:password@tcp(127.0.0.1:3306)/database", + "table": "The table or table globbing patterns in the database", + "cache": "Generate code with cache [optional]", + "dir": "{{.goctl.model.dir}}", + "style": "{{.global.style}}", + "idea": "For idea plugin [optional]", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}" + }, + "ddl": { + "short": "Generate mysql model from ddl", + "src": "The path or path globbing patterns of the ddl", + "dir": "{{.goctl.model.dir}}", + "style": "{{.global.style}}", + "cache": "Generate code with cache [optional]", + "idea": "For idea plugin [optional]", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}" + } + }, + "pg": { + "short": "Generate postgresql model", + "datasource": { + "short": "Generate model from datasource", + "url": "The data source of database,like \"root:password@tcp(127.0.0.1:3306)/database", + "table": "The table or table globbing patterns in the database", + "schema": "The schema or schema globbing patterns in the database", + "cache": "Generate code with cache [optional]", + "dir": "{{.goctl.model.dir}}", + "style": "{{.global.style}}", + "idea": "For idea plugin [optional]", + "strict": "Generate model in strict mode", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}" + } + }, + "mongo": { + "short": "Generate mongo model", + "type": "Specified model type name", + "cache": "Generate code with cache [optional]", + "easy": "Generate code with auto generated CollectionName for easy declare [optional]", + "dir": "{{.goctl.model.dir}}", + "style": "{{.global.style}}", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}" + } + }, + "migrate": { + "short": "Migrate from tal-tech to zeromicro", + "long": "Migrate is a transition command to help users migrate their projects from tal-tech to zeromicro version", + "verbose": "Verbose enables extra logging", + "version": "The target release version of github.com/zeromicro/go-zero to migrate" + }, + "quickstart": { + "short": "quickly start a project", + "service-type": "specify the service type, supported values: [mono, micro]" + }, + "rpc": { + "short": "Generate rpc code", + "o": "Output a sample proto file", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "new": { + "short": "Generate rpc demo service", + "style": "{{.global.style}}", + "idea": "For idea plugin [optional]", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "verbose": "Enable log output" + }, + "template": { + "short": "Generate proto template", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}" + }, + "protoc": { + "short": "Generate grpc code", + "example": "goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.", + "multiple": "Generated in multiple rpc service mode", + "zrpc_out": "The zrpc output directory", + "style": "{{.global.style}}", + "home": "{{.global.home}}", + "remote": "{{.global.remote}}", + "branch": "{{.global.branch}}", + "verbose": "Enable log output" + } + }, + "template": { + "short": "Template operation", + "home": "The goctl home path of the template", + "init": { + "short": "Initialize the all templates(force update)", + "home": "{{.goctl.template.home}}", + "category": "The category of template, enum [api,rpc,model,docker,kube]" + }, + "clean": { + "short": "Clean the all cache templates", + "home": "{{.goctl.template.home}}" + }, + "update": { + "short": "Update template of the target category to the latest", + "home": "{{.goctl.template.home}}", + "category": "{{.goctl.template.category}}" + }, + "revert": { + "short": "Revert the target template to the latest", + "home": "{{.goctl.template.home}}", + "category": "{{.goctl.template.category}}", + "name": "The target file name of template" + } + }, + "upgrade": { + "short": "Upgrade goctl to latest version" + } + }, + "global": { + "home": "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority", + "remote": "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure", + "branch": "The branch of the remote repo, it does work with --remote", + "style": "The file naming format, see [https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md]" + } +} \ No newline at end of file diff --git a/tools/goctl/internal/flags/flags.go b/tools/goctl/internal/flags/flags.go new file mode 100644 index 00000000..40fe557b --- /dev/null +++ b/tools/goctl/internal/flags/flags.go @@ -0,0 +1,101 @@ +package flags + +import ( + "bytes" + _ "embed" + "encoding/json" + "fmt" + "io" + "log" + "strings" + "testing" + + "github.com/zeromicro/go-zero/tools/goctl/util" +) + +//go:embed default_en.json +var defaultEnFlagConfig []byte + +type ConfigLoader struct { + conf map[string]any +} + +func (cl *ConfigLoader) ReadConfig(in io.Reader) error { + return json.NewDecoder(in).Decode(&cl.conf) +} + +func (cl *ConfigLoader) GetString(key string) string { + keyList := strings.FieldsFunc(key, func(r rune) bool { + return r == '.' + }) + var conf = cl.conf + for idx, k := range keyList { + val, ok := conf[k] + if !ok { + return "" + } + if idx < len(keyList)-1 { + conf, ok = val.(map[string]any) + if !ok { + return "" + } + continue + } + + return fmt.Sprint(val) + } + return "" +} + +type Flags struct { + loader *ConfigLoader +} + +func MustLoad() *Flags { + loader := &ConfigLoader{ + conf: map[string]any{}, + } + if err := loader.ReadConfig(bytes.NewBuffer(defaultEnFlagConfig)); err != nil { + log.Fatal(err) + } + + return &Flags{ + loader: loader, + } +} + +func setTestData(t *testing.T, data []byte) { + origin := defaultEnFlagConfig + defaultEnFlagConfig = data + t.Cleanup(func() { + defaultEnFlagConfig = origin + }) +} + +func (f *Flags) Get(key string) (string, error) { + value := f.loader.GetString(key) + for util.IsTemplateVariable(value) { + value = util.TemplateVariable(value) + if value == key { + return "", fmt.Errorf("the variable can not be self: %q", key) + } + return f.Get(value) + } + return value, nil +} + +var flags *Flags + +func Get(key string) string { + if flags == nil { + flags = MustLoad() + } + + v, err := flags.Get(key) + if err != nil { + log.Fatal(err) + return "" + } + + return v +} diff --git a/tools/goctl/internal/flags/flags_test.go b/tools/goctl/internal/flags/flags_test.go new file mode 100644 index 00000000..e54ca8c6 --- /dev/null +++ b/tools/goctl/internal/flags/flags_test.go @@ -0,0 +1,78 @@ +package flags + +import ( + "fmt" + "testing" + + "github.com/zeromicro/go-zero/tools/goctl/test" +) + +func TestFlags_Get(t *testing.T) { + setTestData(t, []byte(`{"host":"0.0.0.0","port":8888,"service":{"host":"{{.host}}","port":"{{.port}}","invalid":"{{.service.invalid}}"}}`)) + f := MustLoad() + executor := test.NewExecutor[string, string]() + executor.Add([]test.Data[string, string]{ + { + Name: "key_host", + Input: "host", + Want: "0.0.0.0", + }, + { + Name: "key_port", + Input: "port", + Want: "8888", + }, + { + Name: "key_service.host", + Input: "service.host", + Want: "0.0.0.0", + }, + { + Name: "key_service.port", + Input: "service.port", + Want: "8888", + }, + { + Name: "key_not_exists", + Input: "service.port.invalid", + }, + { + Name: "key_service.invalid", + Input: "service.invalid", + E: fmt.Errorf("the variable can not be self: %q", "service.invalid"), + }, + }...) + executor.RunE(t, f.Get) +} + +func Test_Get(t *testing.T) { + setTestData(t, []byte(`{"host":"0.0.0.0","port":8888,"service":{"host":"{{.host}}","port":"{{.port}}","invalid":"{{.service.invalid}}"}}`)) + executor := test.NewExecutor[string, string]() + executor.Add([]test.Data[string, string]{ + { + Name: "key_host", + Input: "host", + Want: "0.0.0.0", + }, + { + Name: "key_port", + Input: "port", + Want: "8888", + }, + { + Name: "key_service.host", + Input: "service.host", + Want: "0.0.0.0", + }, + { + Name: "key_service.port", + Input: "service.port", + Want: "8888", + }, + { + Name: "key_not_exists", + Input: "service.port.invalid", + }, + }...) + executor.Run(t, Get) +} diff --git a/tools/goctl/kube/cmd.go b/tools/goctl/kube/cmd.go index 0f5e8746..12bb70d3 100644 --- a/tools/goctl/kube/cmd.go +++ b/tools/goctl/kube/cmd.go @@ -1,6 +1,6 @@ package kube -import "github.com/spf13/cobra" +import "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" var ( varStringName string @@ -26,50 +26,38 @@ var ( varStringImagePullPolicy string // Cmd describes a kube command. - Cmd = &cobra.Command{ - Use: "kube", - Short: "Generate kubernetes files", - } - - deployCmd = &cobra.Command{ - Use: "deploy", - Short: "Generate deployment yaml file", - RunE: deploymentCommand, - } + Cmd = cobrax.NewCommand("kube") + deployCmd = cobrax.NewCommand("deploy", cobrax.WithRunE(deploymentCommand)) ) func init() { - deployCmd.Flags().StringVar(&varStringName, "name", "", "The name of deployment (required)") - deployCmd.Flags().StringVar(&varStringNamespace, "namespace", "", "The namespace of deployment (required)") - deployCmd.Flags().StringVar(&varStringImage, "image", "", "The docker image of deployment (required)") - deployCmd.Flags().StringVar(&varStringSecret, "secret", "", "The secret to image pull from registry") - deployCmd.Flags().IntVar(&varIntRequestCpu, "requestCpu", 500, "The request cpu to deploy") - deployCmd.Flags().IntVar(&varIntRequestMem, "requestMem", 512, "The request memory to deploy") - deployCmd.Flags().IntVar(&varIntLimitCpu, "limitCpu", 1000, "The limit cpu to deploy") - deployCmd.Flags().IntVar(&varIntLimitMem, "limitMem", 1024, "The limit memory to deploy") - deployCmd.Flags().StringVar(&varStringO, "o", "", "The output yaml file (required)") - deployCmd.Flags().IntVar(&varIntReplicas, "replicas", 3, "The number of replicas to deploy") - deployCmd.Flags().IntVar(&varIntRevisions, "revisions", 5, "The number of revision history to limit") - deployCmd.Flags().IntVar(&varIntPort, "port", 0, "The port of the deployment to listen on pod (required)") - deployCmd.Flags().IntVar(&varIntNodePort, "nodePort", 0, "The nodePort of the deployment to expose") - deployCmd.Flags().IntVar(&varIntTargetPort, "targetPort", 0, "The targetPort of the deployment, default to port") - deployCmd.Flags().IntVar(&varIntMinReplicas, "minReplicas", 3, "The min replicas to deploy") - deployCmd.Flags().IntVar(&varIntMaxReplicas, "maxReplicas", 10, "The max replicas to deploy") - deployCmd.Flags().StringVar(&varStringImagePullPolicy, "imagePullPolicy", "", "Image pull policy. One of Always, Never, IfNotPresent") + deployCmdFlags := deployCmd.Flags() + deployCmdFlags.StringVar(&varStringName, "name") + deployCmdFlags.StringVar(&varStringNamespace, "namespace") + deployCmdFlags.StringVar(&varStringImage, "image") + deployCmdFlags.StringVar(&varStringSecret, "secret") + deployCmdFlags.IntVarWithDefaultValue(&varIntRequestCpu, "requestCpu", 500) + deployCmdFlags.IntVarWithDefaultValue(&varIntRequestMem, "requestMem", 512) + deployCmdFlags.IntVarWithDefaultValue(&varIntLimitCpu, "limitCpu", 1000) + deployCmdFlags.IntVarWithDefaultValue(&varIntLimitMem, "limitMem", 1024) + deployCmdFlags.StringVar(&varStringO, "o") + deployCmdFlags.IntVarWithDefaultValue(&varIntReplicas, "replicas", 3) + deployCmdFlags.IntVarWithDefaultValue(&varIntRevisions, "revisions", 5) + deployCmdFlags.IntVar(&varIntPort, "port") + deployCmdFlags.IntVar(&varIntNodePort, "nodePort") + deployCmdFlags.IntVar(&varIntTargetPort, "targetPort") + deployCmdFlags.IntVarWithDefaultValue(&varIntMinReplicas, "minReplicas", 3) + deployCmdFlags.IntVarWithDefaultValue(&varIntMaxReplicas, "maxReplicas", 10) + deployCmdFlags.StringVar(&varStringImagePullPolicy, "imagePullPolicy") + deployCmdFlags.StringVar(&varStringHome, "home") + deployCmdFlags.StringVar(&varStringRemote, "remote") + deployCmdFlags.StringVar(&varStringBranch, "branch") + deployCmdFlags.StringVar(&varStringServiceAccount, "serviceAccount") - deployCmd.Flags().StringVar(&varStringHome, "home", "", "The goctl home path of the template, "+ - "--home and --remote cannot be set at the same time, if they are, --remote has higher priority") - deployCmd.Flags().StringVar(&varStringRemote, "remote", "", "The remote git repo of the template, "+ - "--home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo "+ - "directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure") - deployCmd.Flags().StringVar(&varStringBranch, "branch", "", "The branch of the remote repo, it "+ - "does work with --remote") - deployCmd.Flags().StringVar(&varStringServiceAccount, "serviceAccount", "", "The ServiceAccount "+ - "for the deployment") - deployCmd.MarkFlagRequired("name") - deployCmd.MarkFlagRequired("namespace") - deployCmd.MarkFlagRequired("o") - deployCmd.MarkFlagRequired("port") + _ = deployCmd.MarkFlagRequired("name") + _ = deployCmd.MarkFlagRequired("namespace") + _ = deployCmd.MarkFlagRequired("o") + _ = deployCmd.MarkFlagRequired("port") Cmd.AddCommand(deployCmd) } diff --git a/tools/goctl/migrate/cmd.go b/tools/goctl/migrate/cmd.go index 48ca1070..ba21fb88 100644 --- a/tools/goctl/migrate/cmd.go +++ b/tools/goctl/migrate/cmd.go @@ -1,23 +1,16 @@ package migrate -import "github.com/spf13/cobra" +import "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" var ( boolVarVerbose bool stringVarVersion string // Cmd describes a migrate command. - Cmd = &cobra.Command{ - Use: "migrate", - Short: "Migrate from tal-tech to zeromicro", - Long: "Migrate is a transition command to help users migrate their " + - "projects from tal-tech to zeromicro version", - RunE: migrate, - } + Cmd = cobrax.NewCommand("migrate", cobrax.WithRunE(migrate)) ) func init() { - Cmd.Flags().BoolVarP(&boolVarVerbose, "verbose", "v", - false, "Verbose enables extra logging") - Cmd.Flags().StringVar(&stringVarVersion, "version", defaultMigrateVersion, - "The target release version of github.com/zeromicro/go-zero to migrate") + migrateCmdFlags := Cmd.Flags() + migrateCmdFlags.BoolVarP(&boolVarVerbose, "verbose", "v") + migrateCmdFlags.StringVarWithDefaultValue(&stringVarVersion, "version", defaultMigrateVersion) } diff --git a/tools/goctl/model/cmd.go b/tools/goctl/model/cmd.go index 4b76cd2b..faaf63e6 100644 --- a/tools/goctl/model/cmd.go +++ b/tools/goctl/model/cmd.go @@ -1,104 +1,75 @@ package model import ( - "github.com/spf13/cobra" - + "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" "github.com/zeromicro/go-zero/tools/goctl/model/mongo" "github.com/zeromicro/go-zero/tools/goctl/model/sql/command" ) var ( // Cmd describes a model command. - Cmd = &cobra.Command{ - Use: "model", - Short: "Generate model code", - } - - mysqlCmd = &cobra.Command{ - Use: "mysql", - Short: "Generate mysql model", - } - - ddlCmd = &cobra.Command{ - Use: "ddl", - Short: "Generate mysql model from ddl", - RunE: command.MysqlDDL, - } - - datasourceCmd = &cobra.Command{ - Use: "datasource", - Short: "Generate model from datasource", - RunE: command.MySqlDataSource, - } - - pgCmd = &cobra.Command{ - Use: "pg", - Short: "Generate postgresql model", - RunE: command.PostgreSqlDataSource, - } - - pgDatasourceCmd = &cobra.Command{ - Use: "datasource", - Short: "Generate model from datasource", - RunE: command.PostgreSqlDataSource, - } - - mongoCmd = &cobra.Command{ - Use: "mongo", - Short: "Generate mongo model", - RunE: mongo.Action, - } + Cmd = cobrax.NewCommand("model") + mysqlCmd = cobrax.NewCommand("mysql") + ddlCmd = cobrax.NewCommand("ddl", cobrax.WithRunE(command.MysqlDDL)) + datasourceCmd = cobrax.NewCommand("datasource", cobrax.WithRunE(command.MySqlDataSource)) + pgCmd = cobrax.NewCommand("pg", cobrax.WithRunE(command.PostgreSqlDataSource)) + pgDatasourceCmd = cobrax.NewCommand("datasource", cobrax.WithRunE(command.PostgreSqlDataSource)) + mongoCmd = cobrax.NewCommand("mongo", cobrax.WithRunE(mongo.Action)) ) func init() { - ddlCmd.Flags().StringVarP(&command.VarStringSrc, "src", "s", "", "The path or path globbing patterns of the ddl") - ddlCmd.Flags().StringVarP(&command.VarStringDir, "dir", "d", "", "The target dir") - ddlCmd.Flags().StringVar(&command.VarStringStyle, "style", "", "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]") - ddlCmd.Flags().BoolVarP(&command.VarBoolCache, "cache", "c", false, "Generate code with cache [optional]") - ddlCmd.Flags().BoolVar(&command.VarBoolIdea, "idea", false, "For idea plugin [optional]") - ddlCmd.Flags().StringVar(&command.VarStringDatabase, "database", "", "The name of database [optional]") - ddlCmd.Flags().StringVar(&command.VarStringHome, "home", "", "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority") - ddlCmd.Flags().StringVar(&command.VarStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure") - ddlCmd.Flags().StringVar(&command.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote") + var ( + ddlCmdFlags = ddlCmd.Flags() + datasourceCmdFlags = datasourceCmd.Flags() + pgDatasourceCmdFlags = pgDatasourceCmd.Flags() + mongoCmdFlags = mongoCmd.Flags() + ) - datasourceCmd.Flags().StringVar(&command.VarStringURL, "url", "", `The data source of database,like "root:password@tcp(127.0.0.1:3306)/database"`) - datasourceCmd.Flags().StringSliceVarP(&command.VarStringSliceTable, "table", "t", nil, "The table or table globbing patterns in the database") - datasourceCmd.Flags().BoolVarP(&command.VarBoolCache, "cache", "c", false, "Generate code with cache [optional]") - datasourceCmd.Flags().StringVarP(&command.VarStringDir, "dir", "d", "", "The target dir") - datasourceCmd.Flags().StringVar(&command.VarStringStyle, "style", "", "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]") - datasourceCmd.Flags().BoolVar(&command.VarBoolIdea, "idea", false, "For idea plugin [optional]") - datasourceCmd.Flags().StringVar(&command.VarStringHome, "home", "", "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority") - datasourceCmd.Flags().StringVar(&command.VarStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure") - datasourceCmd.Flags().StringVar(&command.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote") + ddlCmdFlags.StringVarP(&command.VarStringSrc, "src", "s") + ddlCmdFlags.StringVarP(&command.VarStringDir, "dir", "d") + ddlCmdFlags.StringVar(&command.VarStringStyle, "style") + ddlCmdFlags.BoolVarP(&command.VarBoolCache, "cache", "c") + ddlCmdFlags.BoolVar(&command.VarBoolIdea, "idea") + ddlCmdFlags.StringVar(&command.VarStringDatabase, "database") + ddlCmdFlags.StringVar(&command.VarStringHome, "home") + ddlCmdFlags.StringVar(&command.VarStringRemote, "remote") + ddlCmdFlags.StringVar(&command.VarStringBranch, "branch") - pgDatasourceCmd.Flags().StringVar(&command.VarStringURL, "url", "", `The data source of database,like "postgres://root:password@127.0.0.1:5432/database?sslmode=disable"`) - pgDatasourceCmd.Flags().StringVarP(&command.VarStringTable, "table", "t", "", "The table or table globbing patterns in the database") - pgDatasourceCmd.Flags().StringVarP(&command.VarStringSchema, "schema", "s", "public", "The table schema") - pgDatasourceCmd.Flags().BoolVarP(&command.VarBoolCache, "cache", "c", false, "Generate code with cache [optional]") - pgDatasourceCmd.Flags().StringVarP(&command.VarStringDir, "dir", "d", "", "The target dir") - pgDatasourceCmd.Flags().StringVar(&command.VarStringStyle, "style", "", "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]") - pgDatasourceCmd.Flags().BoolVar(&command.VarBoolIdea, "idea", false, "For idea plugin [optional]") - pgDatasourceCmd.Flags().BoolVar(&command.VarBoolStrict, "strict", false, "Generate model in strict mode") - pgDatasourceCmd.Flags().StringVar(&command.VarStringHome, "home", "", "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority") - pgDatasourceCmd.Flags().StringVar(&command.VarStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\n\tThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure") - pgDatasourceCmd.Flags().StringVar(&command.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote") + datasourceCmdFlags.StringVar(&command.VarStringURL, "url") + datasourceCmdFlags.StringSliceVarP(&command.VarStringSliceTable, "table", "t") + datasourceCmdFlags.BoolVarP(&command.VarBoolCache, "cache", "c") + datasourceCmdFlags.StringVarP(&command.VarStringDir, "dir", "d") + datasourceCmdFlags.StringVar(&command.VarStringStyle, "style") + datasourceCmdFlags.BoolVar(&command.VarBoolIdea, "idea") + datasourceCmdFlags.StringVar(&command.VarStringHome, "home") + datasourceCmdFlags.StringVar(&command.VarStringRemote, "remote") + datasourceCmdFlags.StringVar(&command.VarStringBranch, "branch") - mongoCmd.Flags().StringSliceVarP(&mongo.VarStringSliceType, "type", "t", nil, "Specified model type name") - mongoCmd.Flags().BoolVarP(&mongo.VarBoolCache, "cache", "c", false, "Generate code with cache [optional]") - mongoCmd.Flags().BoolVarP(&mongo.VarBoolEasy, "easy", "e", false, "Generate code with auto generated CollectionName for easy declare [optional]") - mongoCmd.Flags().StringVarP(&mongo.VarStringDir, "dir", "d", "", "The target dir") - mongoCmd.Flags().StringVar(&mongo.VarStringStyle, "style", "", "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]") - mongoCmd.Flags().StringVar(&mongo.VarStringHome, "home", "", "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority") - mongoCmd.Flags().StringVar(&mongo.VarStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure") - mongoCmd.Flags().StringVar(&mongo.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote") + pgDatasourceCmdFlags.StringVar(&command.VarStringURL, "url") + pgDatasourceCmdFlags.StringVarP(&command.VarStringTable, "table", "t") + pgDatasourceCmdFlags.StringVarPWithDefaultValue(&command.VarStringSchema, "schema", "s", "public") + pgDatasourceCmdFlags.BoolVarP(&command.VarBoolCache, "cache", "c") + pgDatasourceCmdFlags.StringVarP(&command.VarStringDir, "dir", "d") + pgDatasourceCmdFlags.StringVar(&command.VarStringStyle, "style") + pgDatasourceCmdFlags.BoolVar(&command.VarBoolIdea, "idea") + pgDatasourceCmdFlags.BoolVar(&command.VarBoolStrict, "strict") + pgDatasourceCmdFlags.StringVar(&command.VarStringHome, "home") + pgDatasourceCmdFlags.StringVar(&command.VarStringRemote, "remote") + pgDatasourceCmdFlags.StringVar(&command.VarStringBranch, "branch") - mysqlCmd.PersistentFlags().BoolVar(&command.VarBoolStrict, "strict", false, "Generate model in strict mode") - mysqlCmd.PersistentFlags().StringSliceVarP(&command.VarStringSliceIgnoreColumns, "ignore-columns", "i", []string{"create_at", "created_at", "create_time", "update_at", "updated_at", "update_time"}, "Ignore columns while creating or updating rows") + mongoCmdFlags.StringSliceVarP(&mongo.VarStringSliceType, "type", "t") + mongoCmdFlags.BoolVarP(&mongo.VarBoolCache, "cache", "c") + mongoCmdFlags.BoolVarP(&mongo.VarBoolEasy, "easy", "e") + mongoCmdFlags.StringVarP(&mongo.VarStringDir, "dir", "d") + mongoCmdFlags.StringVar(&mongo.VarStringStyle, "style") + mongoCmdFlags.StringVar(&mongo.VarStringHome, "home") + mongoCmdFlags.StringVar(&mongo.VarStringRemote, "remote") + mongoCmdFlags.StringVar(&mongo.VarStringBranch, "branch") - mysqlCmd.AddCommand(datasourceCmd) - mysqlCmd.AddCommand(ddlCmd) + mysqlCmd.PersistentFlags().BoolVar(&command.VarBoolStrict, "strict") + mysqlCmd.PersistentFlags().StringSliceVarPWithDefaultValue(&command.VarStringSliceIgnoreColumns, "ignore-columns", "i", []string{"create_at", "created_at", "create_time", "update_at", "updated_at", "update_time"}) + + mysqlCmd.AddCommand(datasourceCmd, ddlCmd) pgCmd.AddCommand(pgDatasourceCmd) - Cmd.AddCommand(mysqlCmd) - Cmd.AddCommand(mongoCmd) - Cmd.AddCommand(pgCmd) + Cmd.AddCommand(mysqlCmd, mongoCmd, pgCmd) } diff --git a/tools/goctl/pkg/env/env.go b/tools/goctl/pkg/env/env.go index adfcca58..a6177840 100644 --- a/tools/goctl/pkg/env/env.go +++ b/tools/goctl/pkg/env/env.go @@ -62,6 +62,7 @@ func init() { experimental := existsEnv.GetOr(GoctlExperimental, ExperimentalOff) goctlEnv.SetKV(GoctlExperimental, experimental) } + if !goctlEnv.HasKey(GoctlHome) { goctlEnv.SetKV(GoctlHome, defaultGoctlHome) } @@ -90,8 +91,20 @@ func init() { goctlEnv.SetKV(ProtocGenGoGRPCVersion, protocGenGoGrpcVer) } -func Print() string { - return strings.Join(goctlEnv.Format(), "\n") +func Print(args ...string) string { + if len(args) == 0 { + return strings.Join(goctlEnv.Format(), "\n") + } + + var values []string + for _, key := range args { + value, ok := goctlEnv.GetString(key) + if !ok { + value = fmt.Sprintf("%s=%%not found%%", key) + } + values = append(values, fmt.Sprintf("%s=%s", key, value)) + } + return strings.Join(values, "\n") } func Get(key string) string { diff --git a/tools/goctl/quickstart/cmd.go b/tools/goctl/quickstart/cmd.go index 357b62a9..53217e28 100644 --- a/tools/goctl/quickstart/cmd.go +++ b/tools/goctl/quickstart/cmd.go @@ -1,6 +1,6 @@ package quickstart -import "github.com/spf13/cobra" +import "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" const ( serviceTypeMono = "mono" @@ -11,15 +11,9 @@ var ( varStringServiceType string // Cmd describes the command to run. - Cmd = &cobra.Command{ - Use: "quickstart", - Short: "quickly start a project", - RunE: run, - } + Cmd = cobrax.NewCommand("quickstart", cobrax.WithRunE(run)) ) func init() { - Cmd.Flags().StringVarP(&varStringServiceType, - "service-type", "t", "mono", - "specify the service type, supported values: [mono, micro]") + Cmd.Flags().StringVarPWithDefaultValue(&varStringServiceType, "service-type", "t", "mono") } diff --git a/tools/goctl/rpc/cmd.go b/tools/goctl/rpc/cmd.go index 22a42fd7..b0482ddd 100644 --- a/tools/goctl/rpc/cmd.go +++ b/tools/goctl/rpc/cmd.go @@ -3,114 +3,70 @@ package rpc import ( "github.com/spf13/cobra" "github.com/zeromicro/go-zero/tools/goctl/config" + "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" "github.com/zeromicro/go-zero/tools/goctl/rpc/cli" ) var ( // Cmd describes a rpc command. - Cmd = &cobra.Command{ - Use: "rpc", - Short: "Generate rpc code", - RunE: func(cmd *cobra.Command, args []string) error { - return cli.RPCTemplate(true) - }, - } + Cmd = cobrax.NewCommand("rpc", cobrax.WithRunE(func(command *cobra.Command, strings []string) error { + return cli.RPCTemplate(true) + })) + templateCmd = cobrax.NewCommand("template", cobrax.WithRunE(func(command *cobra.Command, strings []string) error { + return cli.RPCTemplate(false) + })) - newCmd = &cobra.Command{ - Use: "new", - Short: "Generate rpc demo service", - Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - RunE: cli.RPCNew, - } - - templateCmd = &cobra.Command{ - Use: "template", - Short: "Generate proto template", - RunE: func(cmd *cobra.Command, args []string) error { - return cli.RPCTemplate(false) - }, - } - - protocCmd = &cobra.Command{ - Use: "protoc", - Short: "Generate grpc code", - Example: "goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.", - Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - RunE: cli.ZRPC, - } + newCmd = cobrax.NewCommand("new", cobrax.WithRunE(cli.RPCNew), cobrax.WithArgs(cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs))) + protocCmd = cobrax.NewCommand("protoc", cobrax.WithRunE(cli.ZRPC), cobrax.WithArgs(cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs))) ) func init() { - Cmd.Flags().StringVar(&cli.VarStringOutput, "o", "", "Output a sample proto file") - Cmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home path of "+ - "the template, --home and --remote cannot be set at the same time, if they are, --remote has"+ - " higher priority") - Cmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote git repo"+ - " of the template, --home and --remote cannot be set at the same time, if they are, --remote"+ - " has higher priority\n\tThe git repo directory must be consistent with the "+ - "https://github.com/zeromicro/go-zero-template directory structure") - Cmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", "The branch of the "+ - "remote repo, it does work with --remote") + var ( + rpcCmdFlags = Cmd.Flags() + newCmdFlags = newCmd.Flags() + protocCmdFlags = protocCmd.Flags() + templateCmdFlags = templateCmd.Flags() + ) - newCmd.Flags().StringSliceVar(&cli.VarStringSliceGoOpt, "go_opt", nil, "") - newCmd.Flags().StringSliceVar(&cli.VarStringSliceGoGRPCOpt, "go-grpc_opt", nil, "") - newCmd.Flags().StringVar(&cli.VarStringStyle, "style", config.DefaultFormat, "The file "+ - "naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]") - newCmd.Flags().BoolVar(&cli.VarBoolIdea, "idea", false, "Whether the command "+ - "execution environment is from idea plugin.") - newCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home path "+ - "of the template, --home and --remote cannot be set at the same time, if they are, --remote "+ - "has higher priority") - newCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote git "+ - "repo of the template, --home and --remote cannot be set at the same time, if they are, "+ - "--remote has higher priority\n\tThe git repo directory must be consistent with the "+ - "https://github.com/zeromicro/go-zero-template directory structure") - newCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", - "The branch of the remote repo, it does work with --remote") - newCmd.Flags().BoolVarP(&cli.VarBoolVerbose, "verbose", "v", false, "Enable log output") - newCmd.Flags().MarkHidden("go_opt") - newCmd.Flags().MarkHidden("go-grpc_opt") + rpcCmdFlags.StringVar(&cli.VarStringOutput, "o") + rpcCmdFlags.StringVar(&cli.VarStringHome, "home") + rpcCmdFlags.StringVar(&cli.VarStringRemote, "remote") + rpcCmdFlags.StringVar(&cli.VarStringBranch, "branch") - protocCmd.Flags().BoolVarP(&cli.VarBoolMultiple, "multiple", "m", false, - "Generated in multiple rpc service mode") - protocCmd.Flags().StringSliceVar(&cli.VarStringSliceGoOut, "go_out", nil, "") - protocCmd.Flags().StringSliceVar(&cli.VarStringSliceGoGRPCOut, "go-grpc_out", nil, "") - protocCmd.Flags().StringSliceVar(&cli.VarStringSliceGoOpt, "go_opt", nil, "") - protocCmd.Flags().StringSliceVar(&cli.VarStringSliceGoGRPCOpt, "go-grpc_opt", nil, "") - protocCmd.Flags().StringSliceVar(&cli.VarStringSlicePlugin, "plugin", nil, "") - protocCmd.Flags().StringSliceVarP(&cli.VarStringSliceProtoPath, "proto_path", "I", nil, "") - protocCmd.Flags().StringVar(&cli.VarStringZRPCOut, "zrpc_out", "", "The zrpc output directory") - protocCmd.Flags().StringVar(&cli.VarStringStyle, "style", config.DefaultFormat, "The file "+ - "naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]") - protocCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home "+ - "path of the template, --home and --remote cannot be set at the same time, if they are, "+ - "--remote has higher priority") - protocCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote "+ - "git repo of the template, --home and --remote cannot be set at the same time, if they are, "+ - "--remote has higher priority\n\tThe git repo directory must be consistent with the "+ - "https://github.com/zeromicro/go-zero-template directory structure") - protocCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", - "The branch of the remote repo, it does work with --remote") - protocCmd.Flags().BoolVarP(&cli.VarBoolVerbose, "verbose", "v", false, "Enable log output") - protocCmd.Flags().MarkHidden("go_out") - protocCmd.Flags().MarkHidden("go-grpc_out") - protocCmd.Flags().MarkHidden("go_opt") - protocCmd.Flags().MarkHidden("go-grpc_opt") - protocCmd.Flags().MarkHidden("plugin") - protocCmd.Flags().MarkHidden("proto_path") + newCmdFlags.StringSliceVar(&cli.VarStringSliceGoOpt, "go_opt") + newCmdFlags.StringSliceVar(&cli.VarStringSliceGoGRPCOpt, "go-grpc_opt") + newCmdFlags.StringVarWithDefaultValue(&cli.VarStringStyle, "style", config.DefaultFormat) + newCmdFlags.BoolVar(&cli.VarBoolIdea, "idea") + newCmdFlags.StringVar(&cli.VarStringHome, "home") + newCmdFlags.StringVar(&cli.VarStringRemote, "remote") + newCmdFlags.StringVar(&cli.VarStringBranch, "branch") + newCmdFlags.BoolVarP(&cli.VarBoolVerbose, "verbose", "v") + newCmdFlags.MarkHidden("go_opt") + newCmdFlags.MarkHidden("go-grpc_opt") - templateCmd.Flags().StringVar(&cli.VarStringOutput, "o", "", "Output a sample proto file") - templateCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home"+ - " path of the template, --home and --remote cannot be set at the same time, if they are, "+ - "--remote has higher priority") - templateCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote "+ - "git repo of the template, --home and --remote cannot be set at the same time, if they are, "+ - "--remote has higher priority\n\tThe git repo directory must be consistent with the "+ - "https://github.com/zeromicro/go-zero-template directory structure") - templateCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", "The branch"+ - " of the remote repo, it does work with --remote") + protocCmdFlags.BoolVarP(&cli.VarBoolMultiple, "multiple", "m") + protocCmdFlags.StringSliceVar(&cli.VarStringSliceGoOut, "go_out") + protocCmdFlags.StringSliceVar(&cli.VarStringSliceGoGRPCOut, "go-grpc_out") + protocCmdFlags.StringSliceVar(&cli.VarStringSliceGoOpt, "go_opt") + protocCmdFlags.StringSliceVar(&cli.VarStringSliceGoGRPCOpt, "go-grpc_opt") + protocCmdFlags.StringSliceVar(&cli.VarStringSlicePlugin, "plugin") + protocCmdFlags.StringSliceVarP(&cli.VarStringSliceProtoPath, "proto_path", "I") + protocCmdFlags.StringVar(&cli.VarStringZRPCOut, "zrpc_out") + protocCmdFlags.StringVar(&cli.VarStringHome, "home") + protocCmdFlags.StringVar(&cli.VarStringRemote, "remote") + protocCmdFlags.StringVar(&cli.VarStringBranch, "branch") + protocCmdFlags.BoolVarP(&cli.VarBoolVerbose, "verbose", "v") + protocCmdFlags.MarkHidden("go_out") + protocCmdFlags.MarkHidden("go-grpc_out") + protocCmdFlags.MarkHidden("go_opt") + protocCmdFlags.MarkHidden("go-grpc_opt") + protocCmdFlags.MarkHidden("plugin") + protocCmdFlags.MarkHidden("proto_path") - Cmd.AddCommand(newCmd) - Cmd.AddCommand(protocCmd) - Cmd.AddCommand(templateCmd) + templateCmdFlags.StringVar(&cli.VarStringOutput, "o") + templateCmdFlags.StringVar(&cli.VarStringHome, "home") + templateCmdFlags.StringVar(&cli.VarStringRemote, "remote") + templateCmdFlags.StringVar(&cli.VarStringBranch, "branch") + + Cmd.AddCommand(newCmd, protocCmd, templateCmd) } diff --git a/tools/goctl/test/test.go b/tools/goctl/test/test.go new file mode 100644 index 00000000..db946b42 --- /dev/null +++ b/tools/goctl/test/test.go @@ -0,0 +1,86 @@ +package test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" +) + +type Data[T, Y any] struct { + Name string + Input T + Want Y + E error +} + +type Option[T, Y any] func(*Executor[T, Y]) +type assertFn[Y any] func(t *testing.T, expected, actual Y) bool + +func WithComparison[T, Y any](comparisonFn assertFn[Y]) Option[T, Y] { + return func(e *Executor[T, Y]) { + e.equalFn = comparisonFn + } +} + +type Executor[T, Y any] struct { + list []Data[T, Y] + equalFn assertFn[Y] +} + +func NewExecutor[T, Y any](opt ...Option[T, Y]) *Executor[T, Y] { + e := &Executor[T, Y]{} + opt = append(opt, WithComparison[T, Y](func(t *testing.T, expected, actual Y) bool { + gotBytes, err := json.Marshal(actual) + if err != nil { + t.Fatal(err) + return false + } + wantBytes, err := json.Marshal(expected) + if err != nil { + t.Fatal(err) + return false + } + return assert.JSONEq(t, string(wantBytes), string(gotBytes)) + })) + + for _, o := range opt { + o(e) + } + return e +} + +func (e *Executor[T, Y]) Add(data ...Data[T, Y]) { + e.list = append(e.list, data...) +} + +func (e *Executor[T, Y]) Run(t *testing.T, do func(T) Y) { + if do == nil { + panic("execution body is nil") + return + } + for _, v := range e.list { + t.Run(v.Name, func(t *testing.T) { + inner := do + e.equalFn(t, v.Want, inner(v.Input)) + }) + } +} + +func (e *Executor[T, Y]) RunE(t *testing.T, do func(T) (Y, error)) { + if do == nil { + panic("execution body is nil") + return + } + for _, v := range e.list { + t.Run(v.Name, func(t *testing.T) { + inner := do + got, err := inner(v.Input) + if v.E != nil { + assert.Equal(t, v.E, err) + return + } + e.equalFn(t, v.Want, got) + }) + } +} diff --git a/tools/goctl/test/test_test.go b/tools/goctl/test/test_test.go new file mode 100644 index 00000000..807037c5 --- /dev/null +++ b/tools/goctl/test/test_test.go @@ -0,0 +1,101 @@ +package test + +import ( + "errors" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestExecutor_Run(t *testing.T) { + executor := NewExecutor[string, string]() + executor.Add([]Data[string, string]{ + { + Name: "empty", + }, + { + Name: "snake_case", + input: "A_B_C", + want: "a_b_c", + }, + { + Name: "camel_case", + input: "AaBbCc", + want: "aabbcc", + }, + }...) + executor.Run(t, func(s string) string { + return strings.ToLower(s) + }) +} + +func TestExecutor_RunE(t *testing.T) { + var dummyError = errors.New("dummy error") + executor := NewExecutor[string, string]() + executor.Add([]Data[string, string]{ + { + Name: "empty", + }, + { + Name: "snake_case", + input: "A_B_C", + want: "a_b_c", + }, + { + Name: "camel_case", + input: "AaBbCc", + want: "aabbcc", + }, + { + Name: "invalid_input", + input: "😄", + E: dummyError, + }, + }...) + executor.RunE(t, func(s string) (string, error) { + for _, r := range s { + if r == '_' || r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' { + continue + } + return "", dummyError + } + return strings.ToLower(s), nil + }) +} + +func TestWithComparison(t *testing.T) { + var dummyError = errors.New("dummy error") + executor := NewExecutor[string, string](WithComparison[string, string](func(t *testing.T, expected, actual string) bool { + return assert.Equal(t, expected, actual) + })) + executor.Add([]Data[string, string]{ + { + Name: "empty", + }, + { + Name: "snake_case", + input: "A_B_C", + want: "a_b_c", + }, + { + Name: "camel_case", + input: "AaBbCc", + want: "aabbcc", + }, + { + Name: "invalid_input", + input: "😄", + E: dummyError, + }, + }...) + executor.RunE(t, func(s string) (string, error) { + for _, r := range s { + if r == '_' || r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' { + continue + } + return "", dummyError + } + return strings.ToLower(s), nil + }) +} diff --git a/tools/goctl/tpl/cmd.go b/tools/goctl/tpl/cmd.go index a6058830..4e2741e2 100644 --- a/tools/goctl/tpl/cmd.go +++ b/tools/goctl/tpl/cmd.go @@ -1,55 +1,27 @@ package tpl -import ( - "github.com/spf13/cobra" -) +import "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" var ( varStringHome string varStringCategory string varStringName string // Cmd describes a template command. - Cmd = &cobra.Command{ - Use: "template", - Short: "Template operation", - } - - initCmd = &cobra.Command{ - Use: "init", - Short: "Initialize the all templates(force update)", - RunE: genTemplates, - } - - cleanCmd = &cobra.Command{ - Use: "clean", - Short: "Clean the all cache templates", - RunE: cleanTemplates, - } - - updateCmd = &cobra.Command{ - Use: "update", - Short: "Update template of the target category to the latest", - RunE: updateTemplates, - } - - revertCmd = &cobra.Command{ - Use: "revert", - Short: "Revert the target template to the latest", - RunE: revertTemplates, - } + Cmd = cobrax.NewCommand("template") + initCmd = cobrax.NewCommand("init", cobrax.WithRunE(genTemplates)) + cleanCmd = cobrax.NewCommand("clean", cobrax.WithRunE(cleanTemplates)) + updateCmd = cobrax.NewCommand("update", cobrax.WithRunE(updateTemplates)) + revertCmd = cobrax.NewCommand("revert", cobrax.WithRunE(revertTemplates)) ) func init() { - initCmd.Flags().StringVar(&varStringHome, "home", "", "The goctl home path of the template") - cleanCmd.Flags().StringVar(&varStringHome, "home", "", "The goctl home path of the template") - updateCmd.Flags().StringVar(&varStringHome, "home", "", "The goctl home path of the template") - updateCmd.Flags().StringVarP(&varStringCategory, "category", "c", "", "The category of template, enum [api,rpc,model,docker,kube]") - revertCmd.Flags().StringVar(&varStringHome, "home", "", "The goctl home path of the template") - revertCmd.Flags().StringVarP(&varStringCategory, "category", "c", "", "The category of template, enum [api,rpc,model,docker,kube]") - revertCmd.Flags().StringVarP(&varStringName, "name", "n", "", "The target file name of template") + initCmd.Flags().StringVar(&varStringHome, "home") + cleanCmd.Flags().StringVar(&varStringHome, "home") + updateCmd.Flags().StringVar(&varStringHome, "home") + updateCmd.Flags().StringVarP(&varStringCategory, "category", "c") + revertCmd.Flags().StringVar(&varStringHome, "home") + revertCmd.Flags().StringVarP(&varStringCategory, "category", "c") + revertCmd.Flags().StringVarP(&varStringName, "name", "n") - Cmd.AddCommand(cleanCmd) - Cmd.AddCommand(initCmd) - Cmd.AddCommand(revertCmd) - Cmd.AddCommand(updateCmd) + Cmd.AddCommand(cleanCmd, initCmd, revertCmd, updateCmd) } diff --git a/tools/goctl/tpl/templates.go b/tools/goctl/tpl/templates.go index 0a7a8499..2171f08b 100644 --- a/tools/goctl/tpl/templates.go +++ b/tools/goctl/tpl/templates.go @@ -11,6 +11,7 @@ import ( "github.com/zeromicro/go-zero/tools/goctl/api/gogen" apinew "github.com/zeromicro/go-zero/tools/goctl/api/new" "github.com/zeromicro/go-zero/tools/goctl/docker" + "github.com/zeromicro/go-zero/tools/goctl/gateway" "github.com/zeromicro/go-zero/tools/goctl/kube" mongogen "github.com/zeromicro/go-zero/tools/goctl/model/mongo/generate" modelgen "github.com/zeromicro/go-zero/tools/goctl/model/sql/gen" @@ -52,6 +53,9 @@ func genTemplates(_ *cobra.Command, _ []string) error { func() error { return apinew.GenTemplates() }, + func() error { + return gateway.GenTemplates() + }, ); err != nil { return err } @@ -104,6 +108,9 @@ func cleanTemplates(_ *cobra.Command, _ []string) error { func() error { return apinew.Clean() }, + func() error { + return gateway.Clean() + }, ) if err != nil { return err @@ -144,6 +151,8 @@ func updateTemplates(_ *cobra.Command, _ []string) (err error) { return apigen.Update() case apinew.Category(): return apinew.Update() + case gateway.Category(): + return gateway.Update() default: err = fmt.Errorf("unexpected category: %s", category) return @@ -181,6 +190,8 @@ func revertTemplates(_ *cobra.Command, _ []string) (err error) { return apigen.RevertTemplate(filename) case apinew.Category(): return apinew.RevertTemplate(filename) + case gateway.Category(): + return gateway.RevertTemplate(filename) default: err = fmt.Errorf("unexpected category: %s", category) return diff --git a/tools/goctl/upgrade/cmd.go b/tools/goctl/upgrade/cmd.go index bcbd6d2a..3495eabe 100644 --- a/tools/goctl/upgrade/cmd.go +++ b/tools/goctl/upgrade/cmd.go @@ -1,10 +1,6 @@ package upgrade -import "github.com/spf13/cobra" +import "github.com/zeromicro/go-zero/tools/goctl/internal/cobrax" // Cmd describes an upgrade command. -var Cmd = &cobra.Command{ - Use: "upgrade", - Short: "Upgrade goctl to latest version", - RunE: upgrade, -} +var Cmd = cobrax.NewCommand("upgrade", cobrax.WithRunE(upgrade)) diff --git a/tools/goctl/util/stringx/string.go b/tools/goctl/util/stringx/string.go index e3109994..d81b43a9 100644 --- a/tools/goctl/util/stringx/string.go +++ b/tools/goctl/util/stringx/string.go @@ -140,3 +140,15 @@ func ContainsAny(s string, runes ...rune) bool { func ContainsWhiteSpace(s string) bool { return ContainsAny(s, WhiteSpace...) } + +func IsWhiteSpace(text string) bool { + if len(text) == 0 { + return true + } + for _, r := range text { + if !unicode.IsSpace(r) { + return false + } + } + return true +} diff --git a/tools/goctl/util/templatex.go b/tools/goctl/util/templatex.go index 0985fb25..981a6af6 100644 --- a/tools/goctl/util/templatex.go +++ b/tools/goctl/util/templatex.go @@ -4,6 +4,7 @@ import ( "bytes" goformat "go/format" "io/ioutil" + "regexp" "text/template" "github.com/zeromicro/go-zero/tools/goctl/internal/errorx" @@ -77,3 +78,18 @@ func (t *DefaultTemplate) Execute(data any) (*bytes.Buffer, error) { buf.Write(formatOutput) return buf, nil } + +// IsTemplateVariable returns true if the text is a template variable. +// The text must start with a dot and be a valid template. +func IsTemplateVariable(text string) bool { + match, _ := regexp.MatchString(`(?m)^{{(\.\w+)+}}$`, text) + return match +} + +// TemplateVariable returns the variable name of the template. +func TemplateVariable(text string) string { + if IsTemplateVariable(text) { + return text[3 : len(text)-2] + } + return "" +} diff --git a/tools/goctl/util/templatex_test.go b/tools/goctl/util/templatex_test.go new file mode 100644 index 00000000..84486dfc --- /dev/null +++ b/tools/goctl/util/templatex_test.go @@ -0,0 +1,93 @@ +package util + +import ( + "testing" + + "github.com/zeromicro/go-zero/tools/goctl/test" +) + +func TestIsTemplate(t *testing.T) { + executor := test.NewExecutor[string, bool]() + executor.Add([]test.Data[string, bool]{ + { + Name: "empty", + Want: false, + }, + { + Name: "invalid", + Input: "{foo}", + Want: false, + }, + { + Name: "invalid", + Input: "{.foo}", + Want: false, + }, + { + Name: "invalid", + Input: "$foo", + Want: false, + }, + { + Name: "invalid", + Input: "{{foo}}", + Want: false, + }, + { + Name: "invalid", + Input: "{{.}}", + Want: false, + }, + { + Name: "valid", + Input: "{{.foo}}", + Want: true, + }, + { + Name: "valid", + Input: "{{.foo.bar}}", + Want: true, + }, + }...) + executor.Run(t, IsTemplateVariable) +} + +func TestTemplateVariable(t *testing.T) { + executor := test.NewExecutor[string, string]() + executor.Add([]test.Data[string, string]{ + { + Name: "empty", + }, + { + Name: "invalid", + Input: "{foo}", + }, + { + Name: "invalid", + Input: "{.foo}", + }, + { + Name: "invalid", + Input: "$foo", + }, + { + Name: "invalid", + Input: "{{foo}}", + }, + { + Name: "invalid", + Input: "{{.}}", + }, + { + Name: "valid", + Input: "{{.foo}}", + Want: "foo", + }, + { + Name: "valid", + Input: "{{.foo.bar}}", + Want: "foo.bar", + }, + }...) + executor.Run(t, TemplateVariable) +}