瀏覽代碼

feat: Replace cli to cobra (#1855)

* Replace cli

* Replace cli

* Replace cli

* Format code

* Add compare case

* Add compare case

* Add compare case

* Support go style flag

* Support go style flag

* Add test case
anqiansong 3 年之前
父節點
當前提交
5383e29ce6
共有 61 個文件被更改,包括 1856 次插入1588 次删除
  1. 19 15
      tools/goctl/api/apigen/gen.go
  2. 1 2
      tools/goctl/api/apigen/template.go
  3. 176 0
      tools/goctl/api/cmd.go
  4. 17 6
      tools/goctl/api/dartgen/gen.go
  5. 11 4
      tools/goctl/api/docgen/gen.go
  6. 20 13
      tools/goctl/api/format/format.go
  7. 23 9
      tools/goctl/api/gogen/gen.go
  8. 1 2
      tools/goctl/api/gogen/template.go
  9. 11 4
      tools/goctl/api/javagen/gen.go
  10. 14 5
      tools/goctl/api/ktgen/cmd.go
  11. 21 21
      tools/goctl/api/new/newservice.go
  12. 1 2
      tools/goctl/api/new/template.go
  13. 20 7
      tools/goctl/api/tsgen/gen.go
  14. 6 3
      tools/goctl/api/validate/validate.go
  15. 2 2
      tools/goctl/bug/bug.go
  16. 11 0
      tools/goctl/bug/cmd.go
  17. 101 0
      tools/goctl/cmd/root.go
  18. 23 0
      tools/goctl/compare/cmd/cmd.go
  19. 11 0
      tools/goctl/compare/compare.go
  20. 7 0
      tools/goctl/compare/make.sh
  21. 17 0
      tools/goctl/compare/testdata/kotlin.api
  22. 470 0
      tools/goctl/compare/testdata/testcase.go
  23. 120 0
      tools/goctl/compare/testdata/testdata.go
  24. 3 0
      tools/goctl/compare/testdata/unformat.api
  25. 34 0
      tools/goctl/compare/testdata/user.sql
  26. 0 77
      tools/goctl/completion/completion.go
  27. 0 12
      tools/goctl/completion/const.go
  28. 0 51
      tools/goctl/completion/script.go
  29. 32 0
      tools/goctl/docker/cmd.go
  30. 11 15
      tools/goctl/docker/docker.go
  31. 1 2
      tools/goctl/docker/template.go
  32. 3 6
      tools/goctl/env/check.go
  33. 46 0
      tools/goctl/env/cmd.go
  34. 4 5
      tools/goctl/env/env.go
  35. 5 5
      tools/goctl/env/install.go
  36. 1 1
      tools/goctl/go.mod
  37. 7 5
      tools/goctl/go.sum
  38. 2 947
      tools/goctl/goctl.go
  39. 1 1
      tools/goctl/internal/version/version.go
  40. 71 0
      tools/goctl/kube/cmd.go
  41. 22 22
      tools/goctl/kube/kube.go
  42. 23 0
      tools/goctl/migrate/cmd.go
  43. 9 11
      tools/goctl/migrate/migrate.go
  44. 91 0
      tools/goctl/model/cmd.go
  45. 1 2
      tools/goctl/model/mongo/generate/template.go
  46. 26 9
      tools/goctl/model/mongo/mongo.go
  47. 62 48
      tools/goctl/model/sql/command/command.go
  48. 2 3
      tools/goctl/model/sql/command/migrationnotes/migrationnotes.go
  49. 1 5
      tools/goctl/model/sql/command/migrationnotes/v1.3.4.go
  50. 1 2
      tools/goctl/model/sql/gen/template.go
  51. 19 8
      tools/goctl/plugin/plugin.go
  52. 46 24
      tools/goctl/rpc/cli/cli.go
  53. 29 79
      tools/goctl/rpc/cli/zrpc.go
  54. 0 125
      tools/goctl/rpc/cli/zrpc_test.go
  55. 90 0
      tools/goctl/rpc/cmd.go
  56. 1 2
      tools/goctl/rpc/generator/template.go
  57. 55 0
      tools/goctl/tpl/cmd.go
  58. 24 24
      tools/goctl/tpl/templates.go
  59. 10 0
      tools/goctl/upgrade/cmd.go
  60. 2 2
      tools/goctl/upgrade/upgrade.go
  61. 18 0
      tools/goctl/util/pathx/file.go

+ 19 - 15
tools/goctl/api/apigen/gen.go

@@ -9,7 +9,7 @@ import (
 	"strings"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/util"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
@@ -17,13 +17,20 @@ import (
 //go:embed api.tpl
 var apiTemplate string
 
-// ApiCommand create api template file
-func ApiCommand(c *cli.Context) error {
-	if c.NumFlags() == 0 {
-		cli.ShowAppHelpAndExit(c, 1)
-	}
+var (
+	// VarStringOutput describes the output.
+	VarStringOutput string
+	// VarStringHome describes the goctl home.
+	VarStringHome string
+	// VarStringRemote describes the remote git repository.
+	VarStringRemote string
+	// VarStringBranch describes the git branch.
+	VarStringBranch string
+)
 
-	apiFile := c.String("o")
+// CreateApiTemplate create api template file
+func CreateApiTemplate(_ *cobra.Command, _ []string) error {
+	apiFile := VarStringOutput
 	if len(apiFile) == 0 {
 		return errors.New("missing -o")
 	}
@@ -34,18 +41,15 @@ func ApiCommand(c *cli.Context) error {
 	}
 	defer fp.Close()
 
-	home := c.String("home")
-	remote := c.String("remote")
-	branch := c.String("branch")
-	if len(remote) > 0 {
-		repo, _ := util.CloneIntoGitHome(remote, branch)
+	if len(VarStringRemote) > 0 {
+		repo, _ := util.CloneIntoGitHome(VarStringRemote, VarStringBranch)
 		if len(repo) > 0 {
-			home = repo
+			VarStringHome = repo
 		}
 	}
 
-	if len(home) > 0 {
-		pathx.RegisterGoctlHome(home)
+	if len(VarStringHome) > 0 {
+		pathx.RegisterGoctlHome(VarStringHome)
 	}
 
 	text, err := pathx.LoadTemplate(category, apiTemplateFile, apiTemplate)

+ 1 - 2
tools/goctl/api/apigen/template.go

@@ -3,7 +3,6 @@ package apigen
 import (
 	"fmt"
 
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
@@ -27,7 +26,7 @@ func Clean() error {
 }
 
 // GenTemplates generates api template files.
-func GenTemplates(_ *cli.Context) error {
+func GenTemplates() error {
 	return pathx.InitTemplates(category, templates)
 }
 

+ 176 - 0
tools/goctl/api/cmd.go

@@ -0,0 +1,176 @@
+package api
+
+import (
+	"github.com/spf13/cobra"
+	"github.com/zeromicro/go-zero/tools/goctl/api/apigen"
+	"github.com/zeromicro/go-zero/tools/goctl/api/dartgen"
+	"github.com/zeromicro/go-zero/tools/goctl/api/docgen"
+	"github.com/zeromicro/go-zero/tools/goctl/api/format"
+	"github.com/zeromicro/go-zero/tools/goctl/api/gogen"
+	"github.com/zeromicro/go-zero/tools/goctl/api/javagen"
+	"github.com/zeromicro/go-zero/tools/goctl/api/ktgen"
+	"github.com/zeromicro/go-zero/tools/goctl/api/new"
+	"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/plugin"
+)
+
+var (
+	// Cmd describes a 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 yaml file",
+		RunE:  gogen.GoCommand,
+	}
+
+	newCmd = &cobra.Command{
+		Use:     "new",
+		Short:   "Fast create api service",
+		Example: "goctl api new [options] service-name",
+		Args:    cobra.ExactValidArgs(1),
+		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",
+		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,
+	}
+)
+
+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\n\tThe git repo directory must be consistent with the"+
+		" https://github.com/zeromicro/go-zero-template directory structure")
+	Cmd.Flags().StringVar(&apigen.VarStringBranch, "branch", "master", "The branch of the "+
+		"remote repo, it does work with --remote")
+
+	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")
+
+	docCmd.Flags().StringVar(&docgen.VarStringDir, "dir", "", "The target dir")
+	docCmd.Flags().StringVar(&docgen.VarStringOutput, "o", "", "The output markdown directory")
+
+	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")
+
+	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\n\tThe git repo directory must be consistent with the "+
+		"https://github.com/zeromicro/go-zero-template directory structure")
+	goCmd.Flags().StringVar(&gogen.VarStringBranch, "branch", "master", "The branch of "+
+		"the remote repo, it does work with --remote")
+	goCmd.Flags().StringVar(&gogen.VarStringStyle, "style", "gozero", "The file naming format,"+
+		" see [https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md]")
+
+	javaCmd.Flags().StringVar(&javagen.VarStringDir, "dir", "", "The target dir")
+	javaCmd.Flags().StringVar(&javagen.VarStringAPI, "api", "", "The api file")
+
+	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")
+
+	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", "master", "The branch of "+
+		"the remote repo, it does work with --remote")
+	newCmd.Flags().StringVar(&new.VarStringStyle, "style", "gozero", "The file naming format,"+
+		" see [https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md]")
+
+	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]")
+
+	tsCmd.Flags().StringVar(&tsgen.VarStringDir, "dir", "", "The target dir")
+	tsCmd.Flags().StringVar(&tsgen.VarStringAPI, "api", "", "The api file")
+	tsCmd.Flags().StringVar(&tsgen.VarStringWebAPI, "webapi", "", "The web api file path")
+	tsCmd.Flags().StringVar(&tsgen.VarStringCaller, "caller", "", "The web api caller")
+	tsCmd.Flags().BoolVar(&tsgen.VarBoolUnWrap, "unwrap", false, "Unwrap the webapi caller for import")
+
+	validateCmd.Flags().StringVar(&validate.VarStringAPI, "api", "", "Validate target api file")
+
+	// 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)
+}

+ 17 - 6
tools/goctl/api/dartgen/gen.go

@@ -5,17 +5,28 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/core/logx"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
 )
 
+var (
+	// VarStringDir describes the directory.
+	VarStringDir string
+	// VarStringAPI defines the API.
+	VarStringAPI string
+	// VarStringLegacy describes whether legacy.
+	VarStringLegacy bool
+	// VarStringHostname defines the hostname.
+	VarStringHostname string
+)
+
 // DartCommand create dart network request code
-func DartCommand(c *cli.Context) error {
-	apiFile := c.String("api")
-	dir := c.String("dir")
-	isLegacy := c.Bool("legacy")
-	hostname := c.String("hostname")
+func DartCommand(_ *cobra.Command, _ []string) error {
+	apiFile := VarStringAPI
+	dir := VarStringDir
+	isLegacy := VarStringLegacy
+	hostname := VarStringHostname
 	if len(apiFile) == 0 {
 		return errors.New("missing -api")
 	}

+ 11 - 4
tools/goctl/api/docgen/gen.go

@@ -7,19 +7,26 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
+var (
+	// VarStringDir describes a directory.
+	VarStringDir string
+	// VarStringOutput describes an output directory.
+	VarStringOutput string
+)
+
 // DocCommand generate Markdown doc file
-func DocCommand(c *cli.Context) error {
-	dir := c.String("dir")
+func DocCommand(_ *cobra.Command, _ []string) error {
+	dir := VarStringDir
 	if len(dir) == 0 {
 		return errors.New("missing -dir")
 	}
 
-	outputDir := c.String("o")
+	outputDir := VarStringOutput
 	if len(outputDir) == 0 {
 		var err error
 		outputDir, err = os.Getwd()

+ 20 - 13
tools/goctl/api/format/format.go

@@ -12,7 +12,7 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/core/errorx"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
 	"github.com/zeromicro/go-zero/tools/goctl/api/util"
@@ -26,30 +26,37 @@ const (
 	rightBrace       = "}"
 )
 
-// GoFormatApi format api file
-func GoFormatApi(c *cli.Context) error {
-	useStdin := c.Bool("stdin")
-	skipCheckDeclare := c.Bool("declare")
-	dir := c.String("dir")
+var (
+	// VarBoolUseStdin describes whether to use stdin or not.
+	VarBoolUseStdin bool
+	// VarBoolSkipCheckDeclare describes whether to skip.
+	VarBoolSkipCheckDeclare bool
+	// VarStringDir describes the directory.
+	VarStringDir string
+	// VarBoolIgnore describes whether to ignore.
+	VarBoolIgnore bool
+)
 
+// GoFormatApi format api file
+func GoFormatApi(_ *cobra.Command, _ []string) error {
 	var be errorx.BatchError
-	if useStdin {
-		if err := apiFormatReader(os.Stdin, dir, skipCheckDeclare); err != nil {
+	if VarBoolUseStdin {
+		if err := apiFormatReader(os.Stdin, VarStringDir, VarBoolSkipCheckDeclare); err != nil {
 			be.Add(err)
 		}
 	} else {
-		if len(dir) == 0 {
+		if len(VarStringDir) == 0 {
 			return errors.New("missing -dir")
 		}
 
-		_, err := os.Lstat(dir)
+		_, err := os.Lstat(VarStringDir)
 		if err != nil {
-			return errors.New(dir + ": No such file or directory")
+			return errors.New(VarStringDir + ": No such file or directory")
 		}
 
-		err = filepath.Walk(dir, func(path string, fi os.FileInfo, errBack error) (err error) {
+		err = filepath.Walk(VarStringDir, func(path string, fi os.FileInfo, errBack error) (err error) {
 			if strings.HasSuffix(path, ".api") {
-				if err := ApiFormatByPath(path, skipCheckDeclare); err != nil {
+				if err := ApiFormatByPath(path, VarBoolSkipCheckDeclare); err != nil {
 					be.Add(util.WrapErr(err, fi.Name()))
 				}
 			}

+ 23 - 9
tools/goctl/api/gogen/gen.go

@@ -12,7 +12,7 @@ import (
 	"time"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/core/logx"
 	apiformat "github.com/zeromicro/go-zero/tools/goctl/api/format"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
@@ -24,16 +24,30 @@ import (
 
 const tmpFile = "%s-%d"
 
-var tmpDir = path.Join(os.TempDir(), "goctl")
+var (
+	tmpDir = path.Join(os.TempDir(), "goctl")
+	// VarStringDir describes the directory.
+	VarStringDir string
+	// VarStringAPI describes the API.
+	VarStringAPI string
+	// VarStringHome describes the go home.
+	VarStringHome string
+	// VarStringRemote describes the remote git repository.
+	VarStringRemote string
+	// VarStringBranch describes the branch.
+	VarStringBranch string
+	// VarStringStyle describes the style of output files.
+	VarStringStyle string
+)
 
 // GoCommand gen go project files from command line
-func GoCommand(c *cli.Context) error {
-	apiFile := c.String("api")
-	dir := c.String("dir")
-	namingStyle := c.String("style")
-	home := c.String("home")
-	remote := c.String("remote")
-	branch := c.String("branch")
+func GoCommand(_ *cobra.Command, _ []string) error {
+	apiFile := VarStringAPI
+	dir := VarStringDir
+	namingStyle := VarStringStyle
+	home := VarStringHome
+	remote := VarStringRemote
+	branch := VarStringBranch
 	if len(remote) > 0 {
 		repo, _ := util.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {

+ 1 - 2
tools/goctl/api/gogen/template.go

@@ -3,7 +3,6 @@ package gogen
 import (
 	"fmt"
 
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
@@ -45,7 +44,7 @@ func Clean() error {
 }
 
 // GenTemplates generates api template files.
-func GenTemplates(_ *cli.Context) error {
+func GenTemplates() error {
 	return pathx.InitTemplates(category, templates)
 }
 

+ 11 - 4
tools/goctl/api/javagen/gen.go

@@ -6,16 +6,23 @@ import (
 	"strings"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/core/logx"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
+var (
+	// VarStringDir describes a directory.
+	VarStringDir string
+	// VarStringAPI describes an API.
+	VarStringAPI string
+)
+
 // JavaCommand generates java code command entrance.
-func JavaCommand(c *cli.Context) error {
-	apiFile := c.String("api")
-	dir := c.String("dir")
+func JavaCommand(_ *cobra.Command, _ []string) error {
+	apiFile := VarStringAPI
+	dir := VarStringDir
 	if len(apiFile) == 0 {
 		return errors.New("missing -api")
 	}

+ 14 - 5
tools/goctl/api/ktgen/cmd.go

@@ -3,21 +3,30 @@ package ktgen
 import (
 	"errors"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
 )
 
+var (
+	// VarStringDir describes a directory.
+	VarStringDir string
+	// VarStringAPI describes an API.
+	VarStringAPI string
+	// VarStringPKG describes a package.
+	VarStringPKG string
+)
+
 // KtCommand generates kotlin code command entrance
-func KtCommand(c *cli.Context) error {
-	apiFile := c.String("api")
+func KtCommand(_ *cobra.Command, _ []string) error {
+	apiFile := VarStringAPI
 	if apiFile == "" {
 		return errors.New("missing -api")
 	}
-	dir := c.String("dir")
+	dir := VarStringDir
 	if dir == "" {
 		return errors.New("missing -dir")
 	}
-	pkg := c.String("pkg")
+	pkg := VarStringPKG
 	if pkg == "" {
 		return errors.New("missing -pkg")
 	}

+ 21 - 21
tools/goctl/api/new/newservice.go

@@ -8,7 +8,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/urfave/cli"
 	"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"
@@ -18,18 +17,22 @@ import (
 //go:embed api.tpl
 var apiTemplate string
 
-// CreateServiceCommand fast create service
-func CreateServiceCommand(c *cli.Context) error {
-	if c.NArg() == 0 {
-		cli.ShowCommandHelpAndExit(c, "new", 1)
-	}
-
-	args := c.Args()
-	dirName := args.First()
+var (
+	// VarStringHome describes the goctl home.
+	VarStringHome string
+	// VarStringRemote describes the remote git repository.
+	VarStringRemote string
+	// VarStringBranch describes the git branch.
+	VarStringBranch string
+	// VarStringStyle describes the style of output files.
+	VarStringStyle string
+)
 
-	dirStyle := c.String("style")
-	if len(dirStyle) == 0 {
-		dirStyle = conf.DefaultFormat
+// CreateServiceCommand fast create service
+func CreateServiceCommand(args []string) error {
+	dirName := args[0]
+	if len(VarStringStyle) == 0 {
+		VarStringStyle = conf.DefaultFormat
 	}
 	if strings.Contains(dirName, "-") {
 		return errors.New("api new command service name not support strikethrough, because this will used by function name")
@@ -55,18 +58,15 @@ func CreateServiceCommand(c *cli.Context) error {
 
 	defer fp.Close()
 
-	home := c.String("home")
-	remote := c.String("remote")
-	branch := c.String("branch")
-	if len(remote) > 0 {
-		repo, _ := util.CloneIntoGitHome(remote, branch)
+	if len(VarStringRemote) > 0 {
+		repo, _ := util.CloneIntoGitHome(VarStringRemote, VarStringBranch)
 		if len(repo) > 0 {
-			home = repo
+			VarStringHome = repo
 		}
 	}
 
-	if len(home) > 0 {
-		pathx.RegisterGoctlHome(home)
+	if len(VarStringHome) > 0 {
+		pathx.RegisterGoctlHome(VarStringHome)
 	}
 
 	text, err := pathx.LoadTemplate(category, apiTemplateFile, apiTemplate)
@@ -82,6 +82,6 @@ func CreateServiceCommand(c *cli.Context) error {
 		return err
 	}
 
-	err = gogen.DoGenProject(apiFilePath, abs, dirStyle)
+	err = gogen.DoGenProject(apiFilePath, abs, VarStringStyle)
 	return err
 }

+ 1 - 2
tools/goctl/api/new/template.go

@@ -3,7 +3,6 @@ package new
 import (
 	"fmt"
 
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
@@ -27,7 +26,7 @@ func Clean() error {
 }
 
 // GenTemplates generates api template files.
-func GenTemplates(_ *cli.Context) error {
+func GenTemplates() error {
 	return pathx.InitTemplates(category, templates)
 }
 

+ 20 - 7
tools/goctl/api/tsgen/gen.go

@@ -5,19 +5,32 @@ import (
 	"fmt"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/core/logx"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
+var (
+	// VarStringDir describes a directory.
+	VarStringDir string
+	// VarStringAPI describes an API file.
+	VarStringAPI string
+	// VarStringWebAPI describes a web API file.
+	VarStringWebAPI string
+	// VarStringCaller describes a caller.
+	VarStringCaller string
+	// VarBoolUnWrap describes whether wrap or not.
+	VarBoolUnWrap bool
+)
+
 // TsCommand provides the entry to generate typescript codes
-func TsCommand(c *cli.Context) error {
-	apiFile := c.String("api")
-	dir := c.String("dir")
-	webAPI := c.String("webapi")
-	caller := c.String("caller")
-	unwrapAPI := c.Bool("unwrap")
+func TsCommand(_ *cobra.Command, _ []string) error {
+	apiFile := VarStringAPI
+	dir := VarStringDir
+	webAPI := VarStringWebAPI
+	caller := VarStringCaller
+	unwrapAPI := VarBoolUnWrap
 	if len(apiFile) == 0 {
 		return errors.New("missing -api")
 	}

+ 6 - 3
tools/goctl/api/validate/validate.go

@@ -5,13 +5,16 @@ import (
 	"fmt"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
 )
 
+// VarStringAPI describes an API.
+var VarStringAPI string
+
 // GoValidateApi verifies whether the api has a syntax error
-func GoValidateApi(c *cli.Context) error {
-	apiFile := c.String("api")
+func GoValidateApi(_ *cobra.Command, _ []string) error {
+	apiFile := VarStringAPI
 
 	if len(apiFile) == 0 {
 		return errors.New("missing -api")

+ 2 - 2
tools/goctl/bug/bug.go

@@ -6,7 +6,7 @@ import (
 	"os/exec"
 	"runtime"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/internal/version"
 )
 
@@ -29,7 +29,7 @@ var openCmd = map[string]string{
 	darwin:  darwinOpen,
 }
 
-func Action(_ *cli.Context) error {
+func runE(_ *cobra.Command, _ []string) error {
 	env := getEnv()
 	content := fmt.Sprintf(issueTemplate, version.BuildVersion, env.string())
 	content = url.QueryEscape(content)

+ 11 - 0
tools/goctl/bug/cmd.go

@@ -0,0 +1,11 @@
+package bug
+
+import "github.com/spf13/cobra"
+
+// Cmd describes a bug command.
+var Cmd = &cobra.Command{
+	Use:   "bug",
+	Short: "Report a bug",
+	Args:  cobra.NoArgs,
+	RunE:  runE,
+}

+ 101 - 0
tools/goctl/cmd/root.go

@@ -0,0 +1,101 @@
+package cmd
+
+import (
+	"fmt"
+	"os"
+	"runtime"
+	"strings"
+
+	"github.com/logrusorgru/aurora"
+	"github.com/spf13/cobra"
+	"github.com/zeromicro/go-zero/tools/goctl/api"
+	"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/internal/version"
+	"github.com/zeromicro/go-zero/tools/goctl/kube"
+	"github.com/zeromicro/go-zero/tools/goctl/migrate"
+	"github.com/zeromicro/go-zero/tools/goctl/model"
+	"github.com/zeromicro/go-zero/tools/goctl/rpc"
+	"github.com/zeromicro/go-zero/tools/goctl/tpl"
+	"github.com/zeromicro/go-zero/tools/goctl/upgrade"
+)
+
+const (
+	codeFailure = 1
+	dash        = "-"
+	doubleDash  = "--"
+	assign      = "="
+)
+
+var rootCmd = &cobra.Command{
+	Use:   "goctl",
+	Short: "A cli tool to generate go-zero code",
+	Long:  "A cli tool to generate api, zrpc, model code",
+}
+
+// Execute executes the given command
+func Execute() {
+	os.Args = supportGoStdFlag(os.Args)
+	if err := rootCmd.Execute(); err != nil {
+		fmt.Println(aurora.Red(err.Error()))
+		os.Exit(codeFailure)
+	}
+}
+
+func supportGoStdFlag(args []string) []string {
+	copyArgs := append([]string(nil), args...)
+	parentCmd, _, err := rootCmd.Traverse(args[:1])
+	if err != nil { // ignore it to let cobra handle the error.
+		return copyArgs
+	}
+
+	for idx, arg := range copyArgs[0:] {
+		parentCmd, _, err = parentCmd.Traverse([]string{arg})
+		if err != nil { // ignore it to let cobra handle the error.
+			break
+		}
+		if !strings.HasPrefix(arg, dash) {
+			continue
+		}
+
+		flagExpr := strings.TrimPrefix(arg, doubleDash)
+		flagExpr = strings.TrimPrefix(flagExpr, dash)
+		flagName, flagValue := flagExpr, ""
+		assignIndex := strings.Index(flagExpr, assign)
+		if assignIndex > 0 {
+			flagName = flagExpr[:assignIndex]
+			flagValue = flagExpr[assignIndex:]
+		}
+
+		f := parentCmd.Flag(flagName)
+		if f == nil {
+			continue
+		}
+		if f.Shorthand == flagName {
+			continue
+		}
+
+		goStyleFlag := doubleDash + f.Name
+		if assignIndex > 0 {
+			goStyleFlag += flagValue
+		}
+
+		copyArgs[idx] = goStyleFlag
+	}
+	return copyArgs
+}
+
+func init() {
+	rootCmd.Version = fmt.Sprintf("%s %s/%s", version.BuildVersion, runtime.GOOS, runtime.GOARCH)
+	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(rpc.Cmd)
+	rootCmd.AddCommand(tpl.Cmd)
+	rootCmd.AddCommand(upgrade.Cmd)
+}

+ 23 - 0
tools/goctl/compare/cmd/cmd.go

@@ -0,0 +1,23 @@
+package cmd
+
+import (
+	"github.com/spf13/cobra"
+	"github.com/zeromicro/go-zero/tools/goctl/compare/testdata"
+	"github.com/zeromicro/go-zero/tools/goctl/util/console"
+)
+
+var rootCmd = &cobra.Command{
+	Use:   "compare",
+	Short: "Compare the goctl commands generated results between urfave and cobra",
+	Args:  cobra.ExactValidArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		dir := args[0]
+		testdata.MustRun(dir)
+	},
+}
+
+func Execute() {
+	if err := rootCmd.Execute(); err != nil {
+		console.Error("%+v", err)
+	}
+}

+ 11 - 0
tools/goctl/compare/compare.go

@@ -0,0 +1,11 @@
+package main
+
+import "github.com/zeromicro/go-zero/tools/goctl/compare/cmd"
+
+// EXPRIMENTAL: compare goctl generated code results between old and new, it will be removed in the feature.
+// TODO: BEFORE RUNNING: export DSN=$datasource, the database must be gozero, and there has no limit for tables.
+// TODO: AFTER RUNNING: diff --recursive old_fs new_fs
+
+func main() {
+	cmd.Execute()
+}

+ 7 - 0
tools/goctl/compare/make.sh

@@ -0,0 +1,7 @@
+#!/bin/bash
+
+wd=`dirname $0`
+GOBIN="$GOPATH/bin"
+EXE=goctl-compare
+go build -o $EXE $wd
+mv $EXE $GOBIN

+ 17 - 0
tools/goctl/compare/testdata/kotlin.api

@@ -0,0 +1,17 @@
+syntax = "v1"
+
+info(
+    title: "type title here"
+    desc: "type desc here"
+    author: "type author here"
+    email: "type email here"
+    version: "type version here"
+)
+
+type req{}
+type reply{}
+
+service kotlin-api{
+    @handler ping
+    post /ping
+}

+ 470 - 0
tools/goctl/compare/testdata/testcase.go

@@ -0,0 +1,470 @@
+package testdata
+
+import _ "embed"
+
+var (
+	//go:embed unformat.api
+	unformatApi string
+	//go:embed kotlin.api
+	kotlinApi string
+	//go:embed user.sql
+	userSql string
+
+	list = Files{
+		{
+			IsDir: true,
+			Path:  "version",
+			Cmd:   "goctl --version",
+		},
+		{
+			IsDir: true,
+			Path:  "api/sample_file/local",
+			Cmd:   "goctl api --o sample.api",
+		},
+		{
+			IsDir: true,
+			Path:  "api/sample_file/local/assign",
+			Cmd:   "goctl api --o=sample.api",
+		},
+		{
+			IsDir: true,
+			Path:  "api/sample_file/local/assign/shorthand",
+			Cmd:   "goctl api -o=sample.api",
+		},
+		{
+			IsDir: true,
+			Path:  "api/sample_file/remote",
+			Cmd:   "goctl api --o sample.api --remote https://github.com/zeromicro/go-zero-template --branch main",
+		},
+		{
+			IsDir: true,
+			Path:  "api/sample_file/remote/shorthand",
+			Cmd:   "goctl api -o sample.api -remote https://github.com/zeromicro/go-zero-template -branch main",
+		},
+		{
+			IsDir: true,
+			Path:  "api/sample_file/remote/assign",
+			Cmd:   "goctl api --o=sample.api --remote https://github.com/zeromicro/go-zero-template --branch=main",
+		},
+		{
+			IsDir: true,
+			Path:  "api/sample_file/remote/assign/shorthand",
+			Cmd:   "goctl api -o=sample.api -remote https://github.com/zeromicro/go-zero-template -branch=main",
+		},
+		{
+			IsDir: true,
+			Path:  "api/dart/legacy/true",
+			Cmd:   "goctl api --o sample.api && goctl api dart --api sample.api --dir . --hostname 127.0.0.1 --legacy true",
+		},
+		{
+			IsDir: true,
+			Path:  "api/dart/legacy/true/shorthand",
+			Cmd:   "goctl api -o sample.api && goctl api dart -api sample.api -dir . -hostname 127.0.0.1 -legacy true",
+		},
+		{
+			IsDir: true,
+			Path:  "api/dart/legacy/true/assign",
+			Cmd:   "goctl api --o=sample.api && goctl api dart --api=sample.api --dir=. --hostname=127.0.0.1 --legacy=true",
+		},
+		{
+			IsDir: true,
+			Path:  "api/dart/legacy/true/assign/shorthand",
+			Cmd:   "goctl api -o=sample.api && goctl api dart -api=sample.api -dir=. -hostname=127.0.0.1 -legacy=true",
+		},
+		{
+			IsDir: true,
+			Path:  "api/dart/legacy/false",
+			Cmd:   "goctl api --o sample.api && goctl api dart --api sample.api --dir . --hostname 127.0.0.1 --legacy true",
+		},
+		{
+			IsDir: true,
+			Path:  "api/dart/legacy/false/shorthand",
+			Cmd:   "goctl api -o sample.api && goctl api dart -api sample.api -dir . -hostname 127.0.0.1 -legacy true",
+		},
+		{
+			IsDir: true,
+			Path:  "api/dart/legacy/false/assign",
+			Cmd:   "goctl api --o=sample.api && goctl api dart --api=sample.api --dir=. --hostname=127.0.0.1 --legacy=true",
+		},
+		{
+			IsDir: true,
+			Path:  "api/dart/legacy/false/assign/shorthand",
+			Cmd:   "goctl api -o=sample.api && goctl api dart -api=sample.api -dir=. -hostname=127.0.0.1 -legacy=true",
+		},
+		{
+			IsDir: true,
+			Path:  "api/doc",
+			Cmd:   "goctl api --o sample.api && goctl api doc --dir . --o .",
+		},
+		{
+			IsDir: true,
+			Path:  "api/doc/shorthand",
+			Cmd:   "goctl api -o sample.api && goctl api doc -dir . -o .",
+		},
+		{
+			IsDir: true,
+			Path:  "api/doc/assign",
+			Cmd:   "goctl api --o=sample.api && goctl api doc --dir=. --o=.",
+		},
+		{
+			IsDir: true,
+			Path:  "api/doc/assign/shorthand",
+			Cmd:   "goctl api -o=sample.api && goctl api doc -dir=. -o=.",
+		},
+		{
+			Path:    "api/format/unformat.api",
+			Content: unformatApi,
+			Cmd:     "goctl api format --dir . --iu",
+		},
+		{
+			Path:    "api/format/shorthand/unformat.api",
+			Content: unformatApi,
+			Cmd:     "goctl api format -dir . -iu",
+		},
+		{
+			Path:    "api/format/assign/unformat.api",
+			Content: unformatApi,
+			Cmd:     "goctl api format --dir=. --iu",
+		},
+		{
+			Path:    "api/format/assign/shorthand/unformat.api",
+			Content: unformatApi,
+			Cmd:     "goctl api format -dir=. -iu",
+		},
+		{
+			IsDir: true,
+			Path:  "api/go/style/default",
+			Cmd:   "goctl api --o sample.api && goctl api go --api sample.api --dir .",
+		},
+		{
+			IsDir: true,
+			Path:  "api/go/style/default/shorthand",
+			Cmd:   "goctl api -o sample.api && goctl api go -api sample.api -dir .",
+		},
+		{
+			IsDir: true,
+			Path:  "api/go/style/assign/default",
+			Cmd:   "goctl api --o=sample.api && goctl api go --api=sample.api --dir=.",
+		},
+		{
+			IsDir: true,
+			Path:  "api/go/style/assign/default/shorthand",
+			Cmd:   "goctl api -o=sample.api && goctl api go -api=sample.api -dir=.",
+		},
+		{
+			IsDir: true,
+			Path:  "api/go/style/goZero",
+			Cmd:   "goctl api --o sample.api && goctl api go --api sample.api --dir . --style goZero",
+		},
+		{
+			IsDir: true,
+			Path:  "api/go/style/goZero/shorthand",
+			Cmd:   "goctl api -o sample.api && goctl api go -api sample.api -dir . -style goZero",
+		},
+		{
+			IsDir: true,
+			Path:  "api/go/style/goZero/assign",
+			Cmd:   "goctl api --o=sample.api && goctl api go --api=sample.api --dir=. --style=goZero",
+		},
+		{
+			IsDir: true,
+			Path:  "api/go/style/goZero/assign/shorthand",
+			Cmd:   "goctl api -o=sample.api && goctl api go -api=sample.api -dir=. -style=goZero",
+		},
+		{
+			IsDir: true,
+			Path:  "api/java",
+			Cmd:   "goctl api --o sample.api && goctl api java --api sample.api --dir .",
+		},
+		{
+			IsDir: true,
+			Path:  "api/java/shorthand",
+			Cmd:   "goctl api -o sample.api && goctl api java -api sample.api -dir .",
+		},
+		{
+			IsDir: true,
+			Path:  "api/java/assign",
+			Cmd:   "goctl api --o=sample.api && goctl api java --api=sample.api --dir=.",
+		},
+		{
+			IsDir: true,
+			Path:  "api/java/shorthand/assign",
+			Cmd:   "goctl api -o=sample.api && goctl api java -api=sample.api -dir=.",
+		},
+		{
+			IsDir: true,
+			Path:  "api/new/style/default",
+			Cmd:   "goctl api new greet",
+		},
+		{
+			IsDir: true,
+			Path:  "api/new/style/goZero",
+			Cmd:   "goctl api new greet --style goZero",
+		},
+		{
+			IsDir: true,
+			Path:  "api/new/style/goZero/assign",
+			Cmd:   "goctl api new greet --style=goZero",
+		},
+		{
+			IsDir: true,
+			Path:  "api/new/style/goZero/shorthand",
+			Cmd:   "goctl api new greet -style goZero",
+		},
+		{
+			IsDir: true,
+			Path:  "api/new/style/goZero/shorthand/assign",
+			Cmd:   "goctl api new greet -style=goZero",
+		},
+		{
+			IsDir: true,
+			Path:  "api/ts",
+			Cmd:   "goctl api --o sample.api && goctl api ts --api sample.api --dir . --unwrap --webapi .",
+		},
+		{
+			IsDir: true,
+			Path:  "api/ts/shorthand",
+			Cmd:   "goctl api -o sample.api && goctl api ts -api sample.api -dir . -unwrap -webapi .",
+		},
+		{
+			IsDir: true,
+			Path:  "api/ts/assign",
+			Cmd:   "goctl api --o=sample.api && goctl api ts --api=sample.api --dir=. --unwrap --webapi=.",
+		},
+		{
+			IsDir: true,
+			Path:  "api/ts/shorthand/assign",
+			Cmd:   "goctl api -o=sample.api && goctl api ts -api=sample.api -dir=. -unwrap -webapi=.",
+		},
+		{
+			IsDir: true,
+			Path:  "api/validate",
+			Cmd:   "goctl api --o sample.api && goctl api validate --api sample.api",
+		},
+		{
+			IsDir: true,
+			Path:  "api/validate/shorthand",
+			Cmd:   "goctl api -o sample.api && goctl api validate -api sample.api",
+		},
+		{
+			IsDir: true,
+			Path:  "api/validate/assign",
+			Cmd:   "goctl api --o=sample.api && goctl api validate --api=sample.api",
+		},
+		{
+			IsDir: true,
+			Path:  "api/validate/shorthand/assign",
+			Cmd:   "goctl api -o=sample.api && goctl api validate -api=sample.api",
+		},
+		{
+			IsDir: true,
+			Path:  "env/show",
+			Cmd:   "goctl env > env.txt",
+		},
+		{
+			IsDir: true,
+			Path:  "env/check",
+			Cmd:   "goctl env check -f -v",
+		},
+		{
+			IsDir: true,
+			Path:  "env/install",
+			Cmd:   "goctl env install -v",
+		},
+		{
+			IsDir: true,
+			Path:  "kube",
+			Cmd:   "goctl kube deploy --image alpine --name foo --namespace foo --o foo.yaml --port 8888",
+		},
+		{
+			IsDir: true,
+			Path:  "kube/shorthand",
+			Cmd:   "goctl kube deploy -image alpine -name foo -namespace foo -o foo.yaml -port 8888",
+		},
+		{
+			IsDir: true,
+			Path:  "kube/assign",
+			Cmd:   "goctl kube deploy --image=alpine --name=foo --namespace=foo --o=foo.yaml --port=8888",
+		},
+		{
+			IsDir: true,
+			Path:  "kube/shorthand/assign",
+			Cmd:   "goctl kube deploy -image=alpine -name=foo -namespace=foo -o=foo.yaml -port=8888",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mongo/cache",
+			Cmd:   "goctl model mongo --dir . --type user --style goZero -c",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mongo/cache/shorthand",
+			Cmd:   "goctl model mongo -dir . -type user -style goZero -c",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mongo/cache/assign",
+			Cmd:   "goctl model mongo --dir=. --type=user --style=goZero -c",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mongo/cache/shorthand/assign",
+			Cmd:   "goctl model mongo -dir=. -type=user -style=goZero -c",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mongo/nocache",
+			Cmd:   "goctl model mongo --dir . --type user",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mongo/nocache/shorthand",
+			Cmd:   "goctl model mongo -dir . -type user",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mongo/nocache/assign",
+			Cmd:   "goctl model mongo --dir=. --type=user",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mongo/nocache/shorthand/assign",
+			Cmd:   "goctl model mongo -dir=. -type=user",
+		},
+		{
+			Content: userSql,
+			Path:    "model/mysql/ddl/user.sql",
+			Cmd:     "goctl model mysql ddl --database user --dir cache --src user.sql -c",
+		},
+		{
+			Content: userSql,
+			Path:    "model/mysql/ddl/shorthand/user.sql",
+			Cmd:     "goctl model mysql ddl -database user -dir cache -src user.sql -c",
+		},
+		{
+			Content: userSql,
+			Path:    "model/mysql/ddl/assign/user.sql",
+			Cmd:     "goctl model mysql ddl --database=user --dir=cache --src=user.sql -c",
+		},
+		{
+			Content: userSql,
+			Path:    "model/mysql/ddl/shorthand/assign/user.sql",
+			Cmd:     "goctl model mysql ddl -database=user -dir=cache -src=user.sql -c",
+		},
+		{
+			Content: userSql,
+			Path:    "model/mysql/ddl/user.sql",
+			Cmd:     "goctl model mysql ddl --database user --dir nocache --src user.sql",
+		},
+		{
+			Content: userSql,
+			Path:    "model/mysql/ddl/shorthand/user.sql",
+			Cmd:     "goctl model mysql ddl -database user -dir nocache -src user.sql",
+		},
+		{
+			Content: userSql,
+			Path:    "model/mysql/ddl/assign/user.sql",
+			Cmd:     "goctl model mysql ddl --database=user --dir=nocache --src=user.sql",
+		},
+		{
+			Content: userSql,
+			Path:    "model/mysql/ddl/shorthand/assign/user.sql",
+			Cmd:     "goctl model mysql ddl -database=user -dir=nocache -src=user.sql",
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource",
+			Cmd:   `goctl model mysql datasource --url $DSN --dir cache --table "*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/shorthand",
+			Cmd:   `goctl model mysql datasource -url $DSN -dir cache -table "*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/shorthand2",
+			Cmd:   `goctl model mysql datasource -url $DSN -dir cache -t "*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/assign",
+			Cmd:   `goctl model mysql datasource --url=$DSN --dir=cache --table="*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/shorthand/assign",
+			Cmd:   `goctl model mysql datasource -url=$DSN -dir=cache -table="*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/shorthand2/assign",
+			Cmd:   `goctl model mysql datasource -url=$DSN -dir=cache -t="*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource",
+			Cmd:   `goctl model mysql datasource --url $DSN --dir nocache --table "*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/shorthand",
+			Cmd:   `goctl model mysql datasource -url $DSN -dir nocache -table "*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/shorthand2",
+			Cmd:   `goctl model mysql datasource -url $DSN -dir nocache -t "*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/assign",
+			Cmd:   `goctl model mysql datasource --url=$DSN --dir=nocache --table="*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/shorthand/assign",
+			Cmd:   `goctl model mysql datasource -url=$DSN -dir=nocache -table="*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "model/mysql/datasource/shorthand2/assign",
+			Cmd:   `goctl model mysql datasource -url=$DSN -dir=nocache -t="*" -c`,
+		},
+		{
+			IsDir: true,
+			Path:  "rpc/new",
+			Cmd:   "goctl rpc new greet",
+		},
+		{
+			IsDir: true,
+			Path:  "rpc/template",
+			Cmd:   "goctl rpc template --o greet.proto",
+		},
+		{
+			IsDir: true,
+			Path:  "rpc/template/shorthand",
+			Cmd:   "goctl rpc template -o greet.proto",
+		},
+		{
+			IsDir: true,
+			Path:  "rpc/template/assign",
+			Cmd:   "goctl rpc template --o=greet.proto",
+		},
+		{
+			IsDir: true,
+			Path:  "rpc/template/shorthand/assign",
+			Cmd:   "goctl rpc template -o=greet.proto",
+		},
+		{
+			IsDir: true,
+			Path:  "rpc/protoc",
+			Cmd:   "goctl rpc template --o greet.proto && goctl rpc protoc greet.proto --go_out . --go-grpc_out . --zrpc_out .",
+		},
+		{
+			IsDir: true,
+			Path:  "rpc/protoc/assign",
+			Cmd:   "goctl rpc template --o=greet.proto && goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=.",
+		},
+	}
+)

+ 120 - 0
tools/goctl/compare/testdata/testdata.go

@@ -0,0 +1,120 @@
+package testdata
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+
+	"github.com/logrusorgru/aurora"
+	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
+)
+
+type (
+	File struct {
+		IsDir        bool
+		Path         string
+		AbsolutePath string
+		Content      string
+		Cmd          string
+	}
+
+	Files []File
+)
+
+func (f File) execute(goctl string) error {
+	printDir := f.Path
+	dir := f.AbsolutePath
+	if !f.IsDir {
+		printDir = filepath.Dir(printDir)
+		dir = filepath.Dir(dir)
+	}
+	printCommand := strings.ReplaceAll(fmt.Sprintf("cd %s && %s", printDir, f.Cmd), "goctl", filepath.Base(goctl))
+	command := strings.ReplaceAll(fmt.Sprintf("cd %s && %s", dir, f.Cmd), "goctl", goctl)
+	fmt.Println(aurora.BrightGreen(printCommand))
+	cmd := exec.Command("sh", "-c", command)
+	cmd.Env = os.Environ()
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	return cmd.Run()
+}
+
+func (fs Files) execute(goctl string) error {
+	for _, f := range fs {
+		err := f.execute(goctl)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func mustGetTestData(baseDir string) (Files, Files) {
+	if len(baseDir) == 0 {
+		dir, err := os.Getwd()
+		if err != nil {
+			log.Fatalln(err)
+		}
+		baseDir = dir
+	}
+	baseDir, err := filepath.Abs(baseDir)
+	if err != nil {
+		return nil, nil
+	}
+	createFile := func(baseDir string, data File) (File, error) {
+		fp := filepath.Join(baseDir, data.Path)
+		dir := filepath.Dir(fp)
+		if data.IsDir {
+			dir = fp
+		}
+		if err := pathx.MkdirIfNotExist(dir); err != nil {
+			return data, err
+		}
+		data.AbsolutePath = fp
+		if data.IsDir {
+			return data, nil
+		}
+
+		return data, ioutil.WriteFile(fp, []byte(data.Content), os.ModePerm)
+	}
+	oldDir := filepath.Join(baseDir, "old_fs")
+	newDir := filepath.Join(baseDir, "new_fs")
+	os.RemoveAll(oldDir)
+	os.RemoveAll(newDir)
+	var oldFiles, newFiles []File
+	for _, data := range list {
+		od, err := createFile(oldDir, data)
+		if err != nil {
+			log.Fatalln(err)
+		}
+		oldFiles = append(oldFiles, od)
+		nd, err := createFile(newDir, data)
+		if err != nil {
+			log.Fatalln(err)
+		}
+		newFiles = append(newFiles, nd)
+	}
+	return oldFiles, newFiles
+}
+
+func MustRun(baseDir string) {
+	oldFiles, newFiles := mustGetTestData(baseDir)
+	goctlOld, err := exec.LookPath("goctl.old")
+	must(err)
+	goctlNew, err := exec.LookPath("goctl")
+	must(err)
+	fmt.Println(aurora.BrightBlue("========================goctl.old======================="))
+	must(oldFiles.execute(goctlOld))
+	fmt.Println()
+	fmt.Println(aurora.BrightBlue("========================goctl.new======================="))
+	must(newFiles.execute(goctlNew))
+}
+
+func must(err error) {
+	if err != nil {
+		log.Fatalln(err)
+	}
+}

+ 3 - 0
tools/goctl/compare/testdata/unformat.api

@@ -0,0 +1,3 @@
+syntax = "v1"
+
+type Foo struct{}

+ 34 - 0
tools/goctl/compare/testdata/user.sql

@@ -0,0 +1,34 @@
+-- 用户表 --
+CREATE TABLE `user`
+(
+    `id`          bigint(10) NOT NULL AUTO_INCREMENT,
+    `user`        varchar(50)                             NOT NULL DEFAULT '' COMMENT '用户',
+    `name`        varchar(255) COLLATE utf8mb4_general_ci NULL COMMENT '用户\t名称',
+    `password`    varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户\n密码',
+    `mobile`      varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
+    `gender`      char(5) COLLATE utf8mb4_general_ci      NOT NULL COMMENT '男|女|未公\r开',
+    `nickname`    varchar(255) COLLATE utf8mb4_general_ci          DEFAULT '' COMMENT '用户昵称',
+    `type`    tinyint(1) COLLATE utf8mb4_general_ci          DEFAULT 0 COMMENT '用户类型',
+    `create_time` timestamp NULL,
+    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `name_index` (`name`),
+    UNIQUE KEY `name_index2` (`name`),
+    UNIQUE KEY `user_index` (`user`),
+    UNIQUE KEY `type_index` (`type`),
+    UNIQUE KEY `mobile_index` (`mobile`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+CREATE TABLE `student`
+(
+    `type`    bigint                           NOT NULL,
+    `class` varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
+    `name`  varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
+    `age`   tinyint                                   DEFAULT NULL,
+    `score` float(10, 0
+) DEFAULT NULL,
+  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` timestamp NULL DEFAULT NULL,
+  PRIMARY KEY (`type`) USING BTREE,
+  UNIQUE KEY `class_name_index` (`class`,`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

+ 0 - 77
tools/goctl/completion/completion.go

@@ -1,77 +0,0 @@
-package completion
-
-import (
-	"bytes"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"runtime"
-
-	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
-	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
-	"github.com/zeromicro/go-zero/tools/goctl/vars"
-)
-
-func Completion(c *cli.Context) error {
-	goos := runtime.GOOS
-	if goos == vars.OsWindows {
-		return fmt.Errorf("%q: only support unix-like OS", goos)
-	}
-
-	name := c.String("name")
-	if len(name) == 0 {
-		name = defaultCompletionFilename
-	}
-	if filepath.IsAbs(name) {
-		return fmt.Errorf("unsupport absolute path: %q", name)
-	}
-
-	home, err := pathx.GetAutoCompleteHome()
-	if err != nil {
-		return err
-	}
-
-	buffer := bytes.NewBuffer(nil)
-	zshF := filepath.Join(home, "zsh", defaultCompletionFilename)
-	err = pathx.MkdirIfNotExist(filepath.Dir(zshF))
-	if err != nil {
-		return err
-	}
-
-	bashF := filepath.Join(home, "bash", defaultCompletionFilename)
-	err = pathx.MkdirIfNotExist(filepath.Dir(bashF))
-	if err != nil {
-		return err
-	}
-
-	flag := magic
-	err = ioutil.WriteFile(zshF, zsh, os.ModePerm)
-	if err == nil {
-		flag |= flagZsh
-	}
-
-	err = ioutil.WriteFile(bashF, bash, os.ModePerm)
-	if err == nil {
-		flag |= flagBash
-	}
-
-	buffer.WriteString(aurora.BrightGreen("generation auto completion success!\n").String())
-	buffer.WriteString(aurora.BrightGreen("executes the following script to setting shell:\n").String())
-	switch flag {
-	case magic | flagZsh:
-		buffer.WriteString(aurora.BrightCyan(fmt.Sprintf("echo PROG=goctl source %s >> ~/.zshrc && source ~/.zshrc", zshF)).String())
-	case magic | flagBash:
-		buffer.WriteString(aurora.BrightCyan(fmt.Sprintf("echo PROG=goctl source %s >> ~/.bashrc && source ~/.bashrc", bashF)).String())
-	case magic | flagZsh | flagBash:
-		buffer.WriteString(aurora.BrightCyan(fmt.Sprintf(`echo PROG=goctl source %s >> ~/.zshrc && source ~/.zshrc
-or
-echo PROG=goctl source %s >> ~/.bashrc && source ~/.bashrc`, zshF, bashF)).String())
-	default:
-		return nil
-	}
-
-	fmt.Println(buffer.String())
-	return nil
-}

+ 0 - 12
tools/goctl/completion/const.go

@@ -1,12 +0,0 @@
-package completion
-
-const (
-	BashCompletionFlag        = `generate-goctl-completion`
-	defaultCompletionFilename = "goctl_autocomplete"
-)
-
-const (
-	magic = 1 << iota
-	flagZsh
-	flagBash
-)

+ 0 - 51
tools/goctl/completion/script.go

@@ -1,51 +0,0 @@
-package completion
-
-import "fmt"
-
-var zsh = []byte(fmt.Sprintf(`#compdef $PROG
-
-_cli_zsh_autocomplete() {
-
-  local -a opts
-  local cur
-  cur=${words[-1]}
-  if [[ "$cur" == "-"* ]]; then
-    opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --%s)}")
-  else
-    opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --%s)}")
-  fi
-
-  if [[ "${opts[1]}" != "" ]]; then
-    _describe 'values' opts
-  else
-    _files
-  fi
-
-  return
-}
-
-compdef _cli_zsh_autocomplete $PROG
-`, BashCompletionFlag, BashCompletionFlag))
-
-var bash = []byte(fmt.Sprintf(`#! /bin/bash
-
-: ${PROG:=$(basename ${BASH_SOURCE})}
-
-_cli_bash_autocomplete() {
-  if [[ "${COMP_WORDS[0]}" != "source" ]]; then
-    local cur opts base
-    COMPREPLY=()
-    cur="${COMP_WORDS[COMP_CWORD]}"
-    if [[ "$cur" == "-"* ]]; then
-      opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --%s )
-    else
-      opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --%s )
-    fi
-    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
-    return 0
-  fi
-}
-
-complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG
-unset PROG
-`, BashCompletionFlag, BashCompletionFlag))

+ 32 - 0
tools/goctl/docker/cmd.go

@@ -0,0 +1,32 @@
+package docker
+
+import "github.com/spf13/cobra"
+
+var (
+	varStringGo      string
+	varStringBase    string
+	varIntPort       int
+	varStringHome    string
+	varStringRemote  string
+	varStringBranch  string
+	varStringVersion string
+	varStringTZ      string
+
+	// Cmd describes a docker command.
+	Cmd = &cobra.Command{
+		Use:   "docker",
+		Short: "Generate Dockerfile",
+		RunE:  dockerCommand,
+	}
+)
+
+func init() {
+	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\n\tThe 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")
+}

+ 11 - 15
tools/goctl/docker/docker.go

@@ -10,7 +10,7 @@ import (
 	"time"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/util"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
@@ -37,24 +37,20 @@ type Docker struct {
 	Timezone    string
 }
 
-// DockerCommand provides the entry for goctl docker
-func DockerCommand(c *cli.Context) (err error) {
+// dockerCommand provides the entry for goctl docker
+func dockerCommand(_ *cobra.Command, _ []string) (err error) {
 	defer func() {
 		if err == nil {
 			fmt.Println(aurora.Green("Done."))
 		}
 	}()
 
-	if c.NumFlags() == 0 {
-		cli.ShowCommandHelpAndExit(c, "docker", 1)
-	}
-
-	goFile := c.String("go")
-	home := c.String("home")
-	version := c.String("version")
-	remote := c.String("remote")
-	branch := c.String("branch")
-	timezone := c.String("tz")
+	goFile := varStringGo
+	home := varStringHome
+	version := varStringVersion
+	remote := varStringRemote
+	branch := varStringBranch
+	timezone := varStringTZ
 	if len(remote) > 0 {
 		repo, _ := util.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {
@@ -78,8 +74,8 @@ func DockerCommand(c *cli.Context) (err error) {
 		return fmt.Errorf("file %q not found", goFile)
 	}
 
-	base := c.String("base")
-	port := c.Int("port")
+	base := varStringBase
+	port := varIntPort
 	if _, err := os.Stat(etcDir); os.IsNotExist(err) {
 		return generateDockerfile(goFile, base, port, version, timezone)
 	}

+ 1 - 2
tools/goctl/docker/template.go

@@ -3,7 +3,6 @@ package docker
 import (
 	_ "embed"
 
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
@@ -21,7 +20,7 @@ func Clean() error {
 }
 
 // GenTemplates creates docker template files
-func GenTemplates(_ *cli.Context) error {
+func GenTemplates() error {
 	return initTemplate()
 }
 

+ 3 - 6
tools/goctl/env/check.go

@@ -5,7 +5,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/pkg/env"
 	"github.com/zeromicro/go-zero/tools/goctl/pkg/protoc"
 	"github.com/zeromicro/go-zero/tools/goctl/pkg/protocgengo"
@@ -37,11 +37,8 @@ var bins = []bin{
 	},
 }
 
-func Check(ctx *cli.Context) error {
-	install := ctx.Bool("install")
-	force := ctx.Bool("force")
-	verbose := ctx.Bool("verbose")
-	return Prepare(install, force, verbose)
+func check(_ *cobra.Command, _ []string) error {
+	return Prepare(boolVarInstall, boolVarForce, boolVarVerbose)
 }
 
 func Prepare(install, force, verbose bool) error {

+ 46 - 0
tools/goctl/env/cmd.go

@@ -0,0 +1,46 @@
+package env
+
+import "github.com/spf13/cobra"
+
+var (
+	sliceVarWriteValue []string
+	boolVarForce       bool
+	boolVarVerbose     bool
+	boolVarInstall     bool
+
+	// Cmd describes a 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,
+	}
+)
+
+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")
+
+	// The sub-command flags
+	checkCmd.Flags().BoolVarP(&boolVarInstall, "install", "i",
+		false, "Install dependencies if not found")
+
+	// Add sub-command
+	Cmd.AddCommand(installCmd)
+	Cmd.AddCommand(checkCmd)
+}

+ 4 - 5
tools/goctl/env/env.go

@@ -3,14 +3,13 @@ package env
 import (
 	"fmt"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/pkg/env"
 )
 
-func Action(c *cli.Context) error {
-	write := c.StringSlice("write")
-	if len(write) > 0 {
-		return env.WriteEnv(write)
+func write(_ *cobra.Command, _ []string) error {
+	if len(sliceVarWriteValue) > 0 {
+		return env.WriteEnv(sliceVarWriteValue)
 	}
 	fmt.Println(env.Print())
 	return nil

+ 5 - 5
tools/goctl/env/install.go

@@ -1,9 +1,9 @@
 package env
 
-import "github.com/urfave/cli"
+import (
+	"github.com/spf13/cobra"
+)
 
-func Install(c *cli.Context) error {
-	force := c.Bool("force")
-	verbose := c.Bool("verbose")
-	return Prepare(true, force, verbose)
+func install(_ *cobra.Command, _ []string) error {
+	return Prepare(true, boolVarForce, boolVarVerbose)
 }

+ 1 - 1
tools/goctl/go.mod

@@ -9,8 +9,8 @@ require (
 	github.com/go-sql-driver/mysql v1.6.0
 	github.com/iancoleman/strcase v0.2.0
 	github.com/logrusorgru/aurora v2.0.3+incompatible
+	github.com/spf13/cobra v1.4.0
 	github.com/stretchr/testify v1.7.0
-	github.com/urfave/cli v1.22.5
 	github.com/zeromicro/antlr v0.0.1
 	github.com/zeromicro/ddl-parser v1.0.3
 	github.com/zeromicro/go-zero v1.3.2

+ 7 - 5
tools/goctl/go.sum

@@ -85,8 +85,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -238,6 +238,8 @@ github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHL
 github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
 github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
 github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
@@ -344,9 +346,8 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -355,7 +356,10 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
+github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -367,8 +371,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
-github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
 github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
 github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=

+ 2 - 947
tools/goctl/goctl.go

@@ -1,958 +1,13 @@
 package main
 
 import (
-	"fmt"
-	"os"
-	"runtime"
-
-	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/core/load"
 	"github.com/zeromicro/go-zero/core/logx"
-	"github.com/zeromicro/go-zero/tools/goctl/api/apigen"
-	"github.com/zeromicro/go-zero/tools/goctl/api/dartgen"
-	"github.com/zeromicro/go-zero/tools/goctl/api/docgen"
-	"github.com/zeromicro/go-zero/tools/goctl/api/format"
-	"github.com/zeromicro/go-zero/tools/goctl/api/gogen"
-	"github.com/zeromicro/go-zero/tools/goctl/api/javagen"
-	"github.com/zeromicro/go-zero/tools/goctl/api/ktgen"
-	"github.com/zeromicro/go-zero/tools/goctl/api/new"
-	"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/bug"
-	"github.com/zeromicro/go-zero/tools/goctl/completion"
-	"github.com/zeromicro/go-zero/tools/goctl/docker"
-	"github.com/zeromicro/go-zero/tools/goctl/env"
-	"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"
-	"github.com/zeromicro/go-zero/tools/goctl/model/mongo"
-	model "github.com/zeromicro/go-zero/tools/goctl/model/sql/command"
-	"github.com/zeromicro/go-zero/tools/goctl/plugin"
-	rpc "github.com/zeromicro/go-zero/tools/goctl/rpc/cli"
-	"github.com/zeromicro/go-zero/tools/goctl/tpl"
-	"github.com/zeromicro/go-zero/tools/goctl/upgrade"
+	"github.com/zeromicro/go-zero/tools/goctl/cmd"
 )
 
-const codeFailure = 1
-
-var commands = []cli.Command{
-	{
-		Name:   "bug",
-		Usage:  "report a bug",
-		Action: bug.Action,
-	},
-	{
-		Name:   "upgrade",
-		Usage:  "upgrade goctl to latest version",
-		Action: upgrade.Upgrade,
-	},
-	{
-		Name:  "env",
-		Usage: "check or edit goctl environment",
-		Flags: []cli.Flag{
-			cli.StringSliceFlag{
-				Name:  "write, w",
-				Usage: "edit goctl environment",
-			},
-		},
-		Subcommands: []cli.Command{
-			{
-				Name:   "install",
-				Usage:  "goctl env installation",
-				Action: env.Install,
-				Flags: []cli.Flag{
-					cli.BoolFlag{
-						Name:  "force,f",
-						Usage: "silent installation of non-existent dependencies",
-					},
-					cli.BoolFlag{
-						Name:  "verbose, v",
-						Usage: "enable log output",
-					},
-				},
-			},
-			{
-				Name:  "check",
-				Usage: "detect goctl env and dependency tools",
-				Flags: []cli.Flag{
-					cli.BoolFlag{
-						Name:  "install, i",
-						Usage: "install dependencies if not found",
-					},
-					cli.BoolFlag{
-						Name:  "force, f",
-						Usage: "silent installation of non-existent dependencies",
-					},
-					cli.BoolFlag{
-						Name:  "verbose, v",
-						Usage: "enable log output",
-					},
-				},
-				Action: env.Check,
-			},
-		},
-		Action: env.Action,
-	},
-	{
-		Name:        "migrate",
-		Usage:       "migrate from tal-tech to zeromicro",
-		Description: "migrate is a transition command to help users migrate their projects from tal-tech to zeromicro version",
-		Action:      migrate.Migrate,
-		Flags: []cli.Flag{
-			cli.BoolFlag{
-				Name:  "verbose, v",
-				Usage: "verbose enables extra logging",
-			},
-			cli.StringFlag{
-				Name:  "version",
-				Usage: "the target release version of github.com/zeromicro/go-zero to migrate",
-			},
-		},
-	},
-	{
-		Name:  "api",
-		Usage: "generate api related files",
-		Flags: []cli.Flag{
-			cli.StringFlag{
-				Name:  "o",
-				Usage: "output a sample api file",
-			},
-			cli.StringFlag{
-				Name: "home",
-				Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-					"if they are, --remote has higher priority",
-			},
-			cli.StringFlag{
-				Name: "remote",
-				Usage: "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",
-			},
-			cli.StringFlag{
-				Name:  "branch",
-				Usage: "the branch of the remote repo, it does work with --remote",
-			},
-		},
-		Action: apigen.ApiCommand,
-		Subcommands: []cli.Command{
-			{
-				Name:      "new",
-				Usage:     "fast create api service",
-				UsageText: "example: goctl api new [options] service-name",
-				Action:    new.CreateServiceCommand,
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name: "home",
-						Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-							"if they are, --remote has higher priority",
-					},
-					cli.StringFlag{
-						Name: "remote",
-						Usage: "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",
-					},
-					cli.StringFlag{
-						Name:  "branch",
-						Usage: "the branch of the remote repo, it does work with --remote",
-					},
-					cli.StringFlag{
-						Name:  "style",
-						Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md]",
-					},
-				},
-			},
-			{
-				Name:  "format",
-				Usage: "format api files",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "dir",
-						Usage: "the format target dir",
-					},
-					cli.BoolFlag{
-						Name:  "iu",
-						Usage: "ignore update",
-					},
-					cli.BoolFlag{
-						Name:  "stdin",
-						Usage: "use stdin to input api doc content, press \"ctrl + d\" to send EOF",
-					},
-					cli.BoolFlag{
-						Name:  "declare",
-						Usage: "use to skip check api types already declare",
-					},
-				},
-				Action: format.GoFormatApi,
-			},
-			{
-				Name:  "validate",
-				Usage: "validate api file",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "api",
-						Usage: "validate target api file",
-					},
-				},
-				Action: validate.GoValidateApi,
-			},
-			{
-				Name:  "doc",
-				Usage: "generate doc files",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "dir",
-						Usage: "the target dir",
-					},
-					cli.StringFlag{
-						Name:     "o",
-						Required: false,
-						Usage:    "the output markdown directory",
-					},
-				},
-				Action: docgen.DocCommand,
-			},
-			{
-				Name:  "go",
-				Usage: "generate go files for provided api in api file",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "dir",
-						Usage: "the target dir",
-					},
-					cli.StringFlag{
-						Name:  "api",
-						Usage: "the api file",
-					},
-					cli.StringFlag{
-						Name:  "style",
-						Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-					},
-					cli.StringFlag{
-						Name: "home",
-						Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-							"if they are, --remote has higher priority",
-					},
-					cli.StringFlag{
-						Name: "remote",
-						Usage: "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",
-					},
-					cli.StringFlag{
-						Name:  "branch",
-						Usage: "the branch of the remote repo, it does work with --remote",
-					},
-				},
-				Action: gogen.GoCommand,
-			},
-			{
-				Name:  "java",
-				Usage: "generate java files for provided api in api file",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "dir",
-						Usage: "the target dir",
-					},
-					cli.StringFlag{
-						Name:  "api",
-						Usage: "the api file",
-					},
-				},
-				Action: javagen.JavaCommand,
-			},
-			{
-				Name:  "ts",
-				Usage: "generate ts files for provided api in api file",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "dir",
-						Usage: "the target dir",
-					},
-					cli.StringFlag{
-						Name:  "api",
-						Usage: "the api file",
-					},
-					cli.StringFlag{
-						Name:  "webapi",
-						Usage: "the web api file path",
-					},
-					cli.StringFlag{
-						Name:  "caller",
-						Usage: "the web api caller",
-					},
-					cli.BoolFlag{
-						Name:  "unwrap",
-						Usage: "unwrap the webapi caller for import",
-					},
-				},
-				Action: tsgen.TsCommand,
-			},
-			{
-				Name:  "dart",
-				Usage: "generate dart files for provided api in api file",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "dir",
-						Usage: "the target dir",
-					},
-					cli.StringFlag{
-						Name:  "api",
-						Usage: "the api file",
-					},
-					cli.BoolFlag{
-						Name:  "legacy",
-						Usage: "legacy generator for flutter v1",
-					},
-					cli.StringFlag{
-						Name:  "hostname",
-						Usage: "hostname of the server",
-					},
-				},
-				Action: dartgen.DartCommand,
-			},
-			{
-				Name:  "kt",
-				Usage: "generate kotlin code for provided api file",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "dir",
-						Usage: "the target directory",
-					},
-					cli.StringFlag{
-						Name:  "api",
-						Usage: "the api file",
-					},
-					cli.StringFlag{
-						Name:  "pkg",
-						Usage: "define package name for kotlin file",
-					},
-				},
-				Action: ktgen.KtCommand,
-			},
-			{
-				Name:  "plugin",
-				Usage: "custom file generator",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "plugin, p",
-						Usage: "the plugin file",
-					},
-					cli.StringFlag{
-						Name:  "dir",
-						Usage: "the target directory",
-					},
-					cli.StringFlag{
-						Name:  "api",
-						Usage: "the api file",
-					},
-					cli.StringFlag{
-						Name:  "style",
-						Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-					},
-				},
-				Action: plugin.PluginCommand,
-			},
-		},
-	},
-	{
-		Name:  "docker",
-		Usage: "generate Dockerfile",
-		Flags: []cli.Flag{
-			cli.StringFlag{
-				Name:  "go",
-				Usage: "the file that contains main function",
-			},
-			cli.StringFlag{
-				Name:  "base",
-				Usage: "the base image to build the docker image, default scratch",
-				Value: "scratch",
-			},
-			cli.IntFlag{
-				Name:  "port",
-				Usage: "the port to expose, default none",
-				Value: 0,
-			},
-			cli.StringFlag{
-				Name: "home",
-				Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-					"if they are, --remote has higher priority",
-			},
-			cli.StringFlag{
-				Name: "remote",
-				Usage: "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",
-			},
-			cli.StringFlag{
-				Name:  "branch",
-				Usage: "the branch of the remote repo, it does work with --remote",
-			},
-			cli.StringFlag{
-				Name:  "version",
-				Usage: "the goctl builder golang image version",
-			},
-			cli.StringFlag{
-				Name:  "tz",
-				Usage: "the timezone of the container",
-				Value: "Asia/Shanghai",
-			},
-		},
-		Action: docker.DockerCommand,
-	},
-	{
-		Name:  "kube",
-		Usage: "generate kubernetes files",
-		Subcommands: []cli.Command{
-			{
-				Name:  "deploy",
-				Usage: "generate deployment yaml file",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:     "name",
-						Usage:    "the name of deployment",
-						Required: true,
-					},
-					cli.StringFlag{
-						Name:     "namespace",
-						Usage:    "the namespace of deployment",
-						Required: true,
-					},
-					cli.StringFlag{
-						Name:     "image",
-						Usage:    "the docker image of deployment",
-						Required: true,
-					},
-					cli.StringFlag{
-						Name:  "secret",
-						Usage: "the secret to image pull from registry",
-					},
-					cli.IntFlag{
-						Name:  "requestCpu",
-						Usage: "the request cpu to deploy",
-						Value: 500,
-					},
-					cli.IntFlag{
-						Name:  "requestMem",
-						Usage: "the request memory to deploy",
-						Value: 512,
-					},
-					cli.IntFlag{
-						Name:  "limitCpu",
-						Usage: "the limit cpu to deploy",
-						Value: 1000,
-					},
-					cli.IntFlag{
-						Name:  "limitMem",
-						Usage: "the limit memory to deploy",
-						Value: 1024,
-					},
-					cli.StringFlag{
-						Name:     "o",
-						Usage:    "the output yaml file",
-						Required: true,
-					},
-					cli.IntFlag{
-						Name:  "replicas",
-						Usage: "the number of replicas to deploy",
-						Value: 3,
-					},
-					cli.IntFlag{
-						Name:  "revisions",
-						Usage: "the number of revision history to limit",
-						Value: 5,
-					},
-					cli.IntFlag{
-						Name:     "port",
-						Usage:    "the port of the deployment to listen on pod",
-						Required: true,
-					},
-					cli.IntFlag{
-						Name:  "nodePort",
-						Usage: "the nodePort of the deployment to expose",
-						Value: 0,
-					},
-					cli.IntFlag{
-						Name:  "minReplicas",
-						Usage: "the min replicas to deploy",
-						Value: 3,
-					},
-					cli.IntFlag{
-						Name:  "maxReplicas",
-						Usage: "the max replicas of deploy",
-						Value: 10,
-					},
-					cli.StringFlag{
-						Name: "home",
-						Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-							"if they are, --remote has higher priority",
-					},
-					cli.StringFlag{
-						Name: "remote",
-						Usage: "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",
-					},
-					cli.StringFlag{
-						Name:  "branch",
-						Usage: "the branch of the remote repo, it does work with --remote",
-					},
-					cli.StringFlag{
-						Name:  "serviceAccount",
-						Usage: "the ServiceAccount for the deployment",
-					},
-				},
-				Action: kube.DeploymentCommand,
-			},
-		},
-	},
-	{
-		Name:  "rpc",
-		Usage: "generate rpc code",
-		Flags: []cli.Flag{
-			cli.StringFlag{
-				Name:  "o",
-				Usage: "output a sample proto file",
-			},
-			cli.StringFlag{
-				Name: "home",
-				Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-					"if they are, --remote has higher priority",
-			},
-			cli.StringFlag{
-				Name: "remote",
-				Usage: "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",
-			},
-			cli.StringFlag{
-				Name:  "branch",
-				Usage: "the branch of the remote repo, it does work with --remote",
-			},
-		},
-		Action: rpc.RPCTemplate,
-		Subcommands: []cli.Command{
-			{
-				Name:      "new",
-				Usage:     `generate rpc demo service`,
-				UsageText: "example: goctl rpc new [options] service-name",
-				Flags: []cli.Flag{
-					cli.StringSliceFlag{
-						Name:   "go_opt",
-						Hidden: true,
-					},
-					cli.StringSliceFlag{
-						Name:   "go-grpc_opt",
-						Hidden: true,
-					},
-					cli.StringFlag{
-						Name:  "style",
-						Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-					},
-					cli.BoolFlag{
-						Name:  "idea",
-						Usage: "whether the command execution environment is from idea plugin. [optional]",
-					},
-					cli.StringFlag{
-						Name: "home",
-						Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-							"if they are, --remote has higher priority",
-					},
-					cli.StringFlag{
-						Name: "remote",
-						Usage: "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",
-					},
-					cli.StringFlag{
-						Name:  "branch",
-						Usage: "the branch of the remote repo, it does work with --remote",
-					},
-					cli.BoolFlag{
-						Name:  "verbose, v",
-						Usage: "enable log output",
-					},
-				},
-				Action: rpc.RPCNew,
-			},
-			{
-				Name:  "template",
-				Usage: `generate proto template`,
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "out, o",
-						Usage: "the target path of proto (deprecated)",
-					},
-					cli.StringFlag{
-						Name: "home",
-						Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time," +
-							" if they are, --remote has higher priority",
-					},
-					cli.StringFlag{
-						Name: "remote",
-						Usage: "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",
-					},
-					cli.StringFlag{
-						Name:  "branch",
-						Usage: "the branch of the remote repo, it does work with --remote",
-					},
-				},
-				Action: rpc.RPCTemplate,
-			},
-			{
-				Name:        "protoc",
-				Usage:       "generate grpc code",
-				UsageText:   `example: goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.`,
-				Description: "for details, see https://go-zero.dev/cn/goctl-rpc.html",
-				Action:      rpc.ZRPC,
-				Flags: []cli.Flag{
-					cli.StringSliceFlag{
-						Name:   "go_out",
-						Hidden: true,
-					},
-					cli.StringSliceFlag{
-						Name:   "go-grpc_out",
-						Hidden: true,
-					},
-					cli.StringSliceFlag{
-						Name:   "go_opt",
-						Hidden: true,
-					},
-					cli.StringSliceFlag{
-						Name:   "go-grpc_opt",
-						Hidden: true,
-					},
-					cli.StringSliceFlag{
-						Name:   "plugin",
-						Hidden: true,
-					},
-					cli.StringSliceFlag{
-						Name:   "proto_path,I",
-						Hidden: true,
-					},
-					cli.StringFlag{
-						Name:  "zrpc_out",
-						Usage: "the zrpc output directory",
-					},
-					cli.StringFlag{
-						Name:  "style",
-						Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-					},
-					cli.StringFlag{
-						Name:  "home",
-						Usage: "the goctl home path of the template",
-					},
-					cli.StringFlag{
-						Name: "remote",
-						Usage: "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",
-					},
-					cli.StringFlag{
-						Name:  "branch",
-						Usage: "the branch of the remote repo, it does work with --remote",
-					},
-					cli.BoolFlag{
-						Name:  "verbose, v",
-						Usage: "enable log output",
-					},
-				},
-			},
-		},
-	},
-	{
-		Name:  "model",
-		Usage: "generate model code",
-		Subcommands: []cli.Command{
-			{
-				Name:  "mysql",
-				Usage: `generate mysql model`,
-				Subcommands: []cli.Command{
-					{
-						Name:  "ddl",
-						Usage: `generate mysql model from ddl`,
-						Flags: []cli.Flag{
-							cli.StringFlag{
-								Name:  "src, s",
-								Usage: "the path or path globbing patterns of the ddl",
-							},
-							cli.StringFlag{
-								Name:  "dir, d",
-								Usage: "the target dir",
-							},
-							cli.StringFlag{
-								Name:  "style",
-								Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-							},
-							cli.BoolFlag{
-								Name:  "cache, c",
-								Usage: "generate code with cache [optional]",
-							},
-							cli.BoolFlag{
-								Name:  "idea",
-								Usage: "for idea plugin [optional]",
-							},
-							cli.StringFlag{
-								Name:  "database, db",
-								Usage: "the name of database [optional]",
-							},
-							cli.StringFlag{
-								Name: "home",
-								Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-									"if they are, --remote has higher priority",
-							},
-							cli.StringFlag{
-								Name: "remote",
-								Usage: "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",
-							},
-							cli.StringFlag{
-								Name:  "branch",
-								Usage: "the branch of the remote repo, it does work with --remote",
-							},
-						},
-						Action: model.MysqlDDL,
-					},
-					{
-						Name:  "datasource",
-						Usage: `generate model from datasource`,
-						Flags: []cli.Flag{
-							cli.StringFlag{
-								Name:  "url",
-								Usage: `the data source of database,like "root:password@tcp(127.0.0.1:3306)/database"`,
-							},
-							cli.StringSliceFlag{
-								Name:  "table, t",
-								Usage: `the table or table globbing patterns in the database`,
-							},
-							cli.BoolFlag{
-								Name:  "cache, c",
-								Usage: "generate code with cache [optional]",
-							},
-							cli.StringFlag{
-								Name:  "dir, d",
-								Usage: "the target dir",
-							},
-							cli.StringFlag{
-								Name:  "style",
-								Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-							},
-							cli.BoolFlag{
-								Name:  "idea",
-								Usage: "for idea plugin [optional]",
-							},
-							cli.StringFlag{
-								Name: "home",
-								Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-									"if they are, --remote has higher priority",
-							},
-							cli.StringFlag{
-								Name: "remote",
-								Usage: "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",
-							},
-							cli.StringFlag{
-								Name:  "branch",
-								Usage: "the branch of the remote repo, it does work with --remote",
-							},
-						},
-						Action: model.MySqlDataSource,
-					},
-				},
-			},
-			{
-				Name:  "pg",
-				Usage: `generate postgresql model`,
-				Subcommands: []cli.Command{
-					{
-						Name:  "datasource",
-						Usage: `generate model from datasource`,
-						Flags: []cli.Flag{
-							cli.StringFlag{
-								Name:  "url",
-								Usage: `the data source of database,like "postgres://root:password@127.0.0.1:5432/database?sslmode=disable"`,
-							},
-							cli.StringFlag{
-								Name:  "table, t",
-								Usage: `the table or table globbing patterns in the database`,
-							},
-							cli.StringFlag{
-								Name:  "schema, s",
-								Usage: `the table schema, default is [public]`,
-							},
-							cli.BoolFlag{
-								Name:  "cache, c",
-								Usage: "generate code with cache [optional]",
-							},
-							cli.StringFlag{
-								Name:  "dir, d",
-								Usage: "the target dir",
-							},
-							cli.StringFlag{
-								Name:  "style",
-								Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-							},
-							cli.BoolFlag{
-								Name:  "idea",
-								Usage: "for idea plugin [optional]",
-							},
-							cli.StringFlag{
-								Name: "home",
-								Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time, " +
-									"if they are, --remote has higher priority",
-							},
-							cli.StringFlag{
-								Name: "remote",
-								Usage: "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",
-							},
-							cli.StringFlag{
-								Name:  "branch",
-								Usage: "the branch of the remote repo, it does work with --remote",
-							},
-						},
-						Action: model.PostgreSqlDataSource,
-					},
-				},
-			},
-			{
-				Name:  "mongo",
-				Usage: `generate mongo model`,
-				Flags: []cli.Flag{
-					cli.StringSliceFlag{
-						Name:  "type, t",
-						Usage: "specified model type name",
-					},
-					cli.BoolFlag{
-						Name:  "cache, c",
-						Usage: "generate code with cache [optional]",
-					},
-					cli.StringFlag{
-						Name:  "dir, d",
-						Usage: "the target dir",
-					},
-					cli.StringFlag{
-						Name:  "style",
-						Usage: "the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]",
-					},
-					cli.StringFlag{
-						Name: "home",
-						Usage: "the goctl home path of the template, --home and --remote cannot be set at the same time," +
-							" if they are, --remote has higher priority",
-					},
-					cli.StringFlag{
-						Name: "remote",
-						Usage: "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",
-					},
-					cli.StringFlag{
-						Name:  "branch",
-						Usage: "the branch of the remote repo, it does work with --remote",
-					},
-				},
-				Action: mongo.Action,
-			},
-		},
-	},
-	{
-		Name:  "template",
-		Usage: "template operation",
-		Subcommands: []cli.Command{
-			{
-				Name:  "init",
-				Usage: "initialize the all templates(force update)",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "home",
-						Usage: "the goctl home path of the template",
-					},
-				},
-				Action: tpl.GenTemplates,
-			},
-			{
-				Name:  "clean",
-				Usage: "clean the all cache templates",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "home",
-						Usage: "the goctl home path of the template",
-					},
-				},
-				Action: tpl.CleanTemplates,
-			},
-			{
-				Name:  "update",
-				Usage: "update template of the target category to the latest",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "category,c",
-						Usage: "the category of template, enum [api,rpc,model,docker,kube]",
-					},
-					cli.StringFlag{
-						Name:  "home",
-						Usage: "the goctl home path of the template",
-					},
-				},
-				Action: tpl.UpdateTemplates,
-			},
-			{
-				Name:  "revert",
-				Usage: "revert the target template to the latest",
-				Flags: []cli.Flag{
-					cli.StringFlag{
-						Name:  "category,c",
-						Usage: "the category of template, enum [api,rpc,model,docker,kube]",
-					},
-					cli.StringFlag{
-						Name:  "name,n",
-						Usage: "the target file name of template",
-					},
-					cli.StringFlag{
-						Name:  "home",
-						Usage: "the goctl home path of the template",
-					},
-				},
-				Action: tpl.RevertTemplates,
-			},
-		},
-	},
-	{
-		Name:   "completion",
-		Usage:  "generation completion script, it only works for unix-like OS",
-		Action: completion.Completion,
-		Flags: []cli.Flag{
-			cli.StringFlag{
-				Name:  "name, n",
-				Usage: "the filename of auto complete script, default is [goctl_autocomplete]",
-			},
-		},
-	},
-}
-
 func main() {
 	logx.Disable()
 	load.Disable()
-
-	cli.BashCompletionFlag = cli.BoolFlag{
-		Name:   completion.BashCompletionFlag,
-		Hidden: true,
-	}
-	app := cli.NewApp()
-	app.EnableBashCompletion = true
-	app.Usage = "a cli tool to generate code"
-	app.Version = fmt.Sprintf("%s %s/%s", version.BuildVersion, runtime.GOOS, runtime.GOARCH)
-	app.Commands = commands
-
-	// cli already print error messages.
-	if err := app.Run(os.Args); err != nil {
-		fmt.Println(aurora.Red(err.Error()))
-		os.Exit(codeFailure)
-	}
+	cmd.Execute()
 }

+ 1 - 1
tools/goctl/internal/version/version.go

@@ -6,7 +6,7 @@ import (
 )
 
 // BuildVersion is the version of goctl.
-const BuildVersion = "1.3.5"
+const BuildVersion = "1.3.6-beta"
 
 var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
 

+ 71 - 0
tools/goctl/kube/cmd.go

@@ -0,0 +1,71 @@
+package kube
+
+import "github.com/spf13/cobra"
+
+var (
+	varStringName           string
+	varStringNamespace      string
+	varStringImage          string
+	varStringSecret         string
+	varIntRequestCpu        int
+	varIntRequestMem        int
+	varIntLimitCpu          int
+	varIntLimitMem          int
+	varStringO              string
+	varIntReplicas          int
+	varIntRevisions         int
+	varIntPort              int
+	varIntNodePort          int
+	varIntMinReplicas       int
+	varIntMaxReplicas       int
+	varStringHome           string
+	varStringRemote         string
+	varStringBranch         string
+	varStringServiceAccount 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,
+	}
+)
+
+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(&varIntMinReplicas, "minReplicas", 3, "The min replicas to deploy")
+	deployCmd.Flags().IntVar(&varIntMaxReplicas, "maxReplicas", 10, "The max replicas to deploy")
+
+	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\n\tThe 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")
+
+	Cmd.AddCommand(deployCmd)
+}

+ 22 - 22
tools/goctl/kube/kube.go

@@ -7,7 +7,7 @@ import (
 	"text/template"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/util"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
@@ -48,11 +48,11 @@ type Deployment struct {
 }
 
 // DeploymentCommand is used to generate the kubernetes deployment yaml files.
-func DeploymentCommand(c *cli.Context) error {
-	nodePort := c.Int("nodePort")
-	home := c.String("home")
-	remote := c.String("remote")
-	branch := c.String("branch")
+func deploymentCommand(_ *cobra.Command, _ []string) error {
+	nodePort := varIntNodePort
+	home := varStringHome
+	remote := varStringRemote
+	branch := varStringBranch
 	if len(remote) > 0 {
 		repo, _ := util.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {
@@ -74,7 +74,7 @@ func DeploymentCommand(c *cli.Context) error {
 		return err
 	}
 
-	out, err := pathx.CreateIfNotExist(c.String("o"))
+	out, err := pathx.CreateIfNotExist(varStringO)
 	if err != nil {
 		return err
 	}
@@ -82,22 +82,22 @@ func DeploymentCommand(c *cli.Context) error {
 
 	t := template.Must(template.New("deploymentTemplate").Parse(text))
 	err = t.Execute(out, Deployment{
-		Name:           c.String("name"),
-		Namespace:      c.String("namespace"),
-		Image:          c.String("image"),
-		Secret:         c.String("secret"),
-		Replicas:       c.Int("replicas"),
-		Revisions:      c.Int("revisions"),
-		Port:           c.Int("port"),
+		Name:           varStringName,
+		Namespace:      varStringNamespace,
+		Image:          varStringImage,
+		Secret:         varStringSecret,
+		Replicas:       varIntReplicas,
+		Revisions:      varIntRevisions,
+		Port:           varIntPort,
 		NodePort:       nodePort,
 		UseNodePort:    nodePort > 0,
-		RequestCpu:     c.Int("requestCpu"),
-		RequestMem:     c.Int("requestMem"),
-		LimitCpu:       c.Int("limitCpu"),
-		LimitMem:       c.Int("limitMem"),
-		MinReplicas:    c.Int("minReplicas"),
-		MaxReplicas:    c.Int("maxReplicas"),
-		ServiceAccount: c.String("serviceAccount"),
+		RequestCpu:     varIntRequestCpu,
+		RequestMem:     varIntRequestMem,
+		LimitCpu:       varIntLimitCpu,
+		LimitMem:       varIntLimitMem,
+		MinReplicas:    varIntMinReplicas,
+		MaxReplicas:    varIntMaxReplicas,
+		ServiceAccount: varStringServiceAccount,
 	})
 	if err != nil {
 		return err
@@ -118,7 +118,7 @@ func Clean() error {
 }
 
 // GenTemplates generates the deployment template files.
-func GenTemplates(_ *cli.Context) error {
+func GenTemplates() error {
 	return pathx.InitTemplates(category, map[string]string{
 		deployTemplateFile: deploymentTemplate,
 		jobTemplateFile:    jobTemplate,

+ 23 - 0
tools/goctl/migrate/cmd.go

@@ -0,0 +1,23 @@
+package migrate
+
+import "github.com/spf13/cobra"
+
+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,
+	}
+)
+
+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")
+}

+ 9 - 11
tools/goctl/migrate/migrate.go

@@ -16,13 +16,13 @@ import (
 	"time"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/util/console"
 	"github.com/zeromicro/go-zero/tools/goctl/util/ctx"
 	"github.com/zeromicro/go-zero/tools/goctl/vars"
 )
 
-const zeromicroVersion = "v1.3.0"
+const defaultMigrateVersion = "v1.3.0"
 
 const (
 	confirmUnknown = iota
@@ -35,28 +35,26 @@ var (
 	builderxConfirm = confirmUnknown
 )
 
-func Migrate(c *cli.Context) error {
-	verbose := c.Bool("verbose")
-	version := c.String("version")
-	if len(version) == 0 {
-		version = zeromicroVersion
+func migrate(_ *cobra.Command, _ []string) error {
+	if len(stringVarVersion) == 0 {
+		stringVarVersion = defaultMigrateVersion
 	}
-	err := editMod(version, verbose)
+	err := editMod(stringVarVersion, boolVarVerbose)
 	if err != nil {
 		return err
 	}
 
-	err = rewriteImport(verbose)
+	err = rewriteImport(boolVarVerbose)
 	if err != nil {
 		return err
 	}
 
-	err = tidy(verbose)
+	err = tidy(boolVarVerbose)
 	if err != nil {
 		return err
 	}
 
-	if verbose {
+	if boolVarVerbose {
 		console.Success("[OK] refactor finish, execute %q on project root to check status.",
 			"go test -race ./...")
 	}

+ 91 - 0
tools/goctl/model/cmd.go

@@ -0,0 +1,91 @@
+package model
+
+import (
+	"github.com/spf13/cobra"
+	"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,
+	}
+
+	mongoCmd = &cobra.Command{
+		Use:   "mongo",
+		Short: "Generate mongo model",
+		RunE:  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\n\tThe 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")
+
+	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\n\tThe 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")
+
+	pgCmd.Flags().StringVar(&command.VarStringURL, "url", "", `The data source of database,like "root:password@tcp(127.0.0.1:3306)/database"`)
+	pgCmd.Flags().StringVarP(&command.VarStringTable, "table", "t", "", "The table or table globbing patterns in the database")
+	pgCmd.Flags().StringVarP(&command.VarStringSchema, "schema", "s", "public", "The table schema")
+	pgCmd.Flags().BoolVarP(&command.VarBoolCache, "cache", "c", false, "Generate code with cache [optional]")
+	pgCmd.Flags().StringVarP(&command.VarStringDir, "dir", "d", "", "The target dir")
+	pgCmd.Flags().StringVar(&command.VarStringStyle, "style", "", "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]")
+	pgCmd.Flags().BoolVar(&command.VarBoolIdea, "idea", false, "For idea plugin [optional]")
+	pgCmd.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")
+	pgCmd.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")
+	pgCmd.Flags().StringVar(&command.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote")
+
+	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().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\n\tThe 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")
+
+	mysqlCmd.AddCommand(datasourceCmd)
+	mysqlCmd.AddCommand(ddlCmd)
+	mysqlCmd.AddCommand(pgCmd)
+	Cmd.AddCommand(mysqlCmd)
+	Cmd.AddCommand(mongoCmd)
+}

+ 1 - 2
tools/goctl/model/mongo/generate/template.go

@@ -3,7 +3,6 @@ package generate
 import (
 	"fmt"
 
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/model/mongo/template"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
@@ -30,7 +29,7 @@ func Clean() error {
 }
 
 // Templates initializes the mongo templates.
-func Templates(_ *cli.Context) error {
+func Templates() error {
 	return pathx.InitTemplates(category, templates)
 }
 

+ 26 - 9
tools/goctl/model/mongo/mongo.go

@@ -5,22 +5,39 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/config"
 	"github.com/zeromicro/go-zero/tools/goctl/model/mongo/generate"
 	file "github.com/zeromicro/go-zero/tools/goctl/util"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
+var (
+	// VarStringSliceType describes a golang data structure name for mongo.
+	VarStringSliceType []string
+	// VarStringDir describes an output directory.
+	VarStringDir string
+	// VarBoolCache describes whether cache is enabled.
+	VarBoolCache bool
+	// VarStringStyle describes the style.
+	VarStringStyle string
+	// VarStringHome describes the goctl home.
+	VarStringHome string
+	// VarStringRemote describes the remote git repository.
+	VarStringRemote string
+	// VarStringBranch describes the git branch.
+	VarStringBranch string
+)
+
 // Action provides the entry for goctl mongo code generation.
-func Action(ctx *cli.Context) error {
-	tp := ctx.StringSlice("type")
-	c := ctx.Bool("cache")
-	o := strings.TrimSpace(ctx.String("dir"))
-	s := ctx.String("style")
-	home := ctx.String("home")
-	remote := ctx.String("remote")
-	branch := ctx.String("branch")
+func Action(_ *cobra.Command, _ []string) error {
+	tp := VarStringSliceType
+	c := VarBoolCache
+	o := strings.TrimSpace(VarStringDir)
+	s := VarStringStyle
+	home := VarStringHome
+	remote := VarStringRemote
+	branch := VarStringBranch
 	if len(remote) > 0 {
 		repo, _ := file.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {

+ 62 - 48
tools/goctl/model/sql/command/command.go

@@ -6,7 +6,7 @@ import (
 	"strings"
 
 	"github.com/go-sql-driver/mysql"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/core/logx"
 	"github.com/zeromicro/go-zero/core/stores/postgres"
 	"github.com/zeromicro/go-zero/core/stores/sqlx"
@@ -20,35 +20,49 @@ import (
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
-const (
-	flagSrc      = "src"
-	flagDir      = "dir"
-	flagCache    = "cache"
-	flagIdea     = "idea"
-	flagURL      = "url"
-	flagTable    = "table"
-	flagStyle    = "style"
-	flagDatabase = "database"
-	flagSchema   = "schema"
-	flagHome     = "home"
-	flagRemote   = "remote"
-	flagBranch   = "branch"
+var (
+	// VarStringSrc describes the source file of sql.
+	VarStringSrc string
+	// VarStringDir describes the output directory of sql.
+	VarStringDir string
+	// VarBoolCache describes whether the cache is enabled.
+	VarBoolCache bool
+	// VarBoolIdea describes whether is idea or not.
+	VarBoolIdea bool
+	// VarStringURL describes the dsn of the sql.
+	VarStringURL string
+	// VarStringSliceTable describes tables.
+	VarStringSliceTable []string
+	// VarStringTable describes a table of sql.
+	VarStringTable string
+	// VarStringStyle describes the style.
+	VarStringStyle string
+	// VarStringDatabase describes the database.
+	VarStringDatabase string
+	// VarStringSchema describes the schema of postgresql.
+	VarStringSchema string
+	// VarStringHome describes the goctl home.
+	VarStringHome string
+	// VarStringRemote describes the remote git repository.
+	VarStringRemote string
+	// VarStringBranch describes the git branch of the repository.
+	VarStringBranch string
 )
 
 var errNotMatched = errors.New("sql not matched")
 
 // MysqlDDL generates model code from ddl
-func MysqlDDL(ctx *cli.Context) error {
-	migrationnotes.BeforeCommands(ctx)
-	src := ctx.String(flagSrc)
-	dir := ctx.String(flagDir)
-	cache := ctx.Bool(flagCache)
-	idea := ctx.Bool(flagIdea)
-	style := ctx.String(flagStyle)
-	database := ctx.String(flagDatabase)
-	home := ctx.String(flagHome)
-	remote := ctx.String(flagRemote)
-	branch := ctx.String(flagBranch)
+func MysqlDDL(_ *cobra.Command, _ []string) error {
+	migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
+	src := VarStringSrc
+	dir := VarStringDir
+	cache := VarBoolCache
+	idea := VarBoolIdea
+	style := VarStringStyle
+	database := VarStringDatabase
+	home := VarStringHome
+	remote := VarStringRemote
+	branch := VarStringBranch
 	if len(remote) > 0 {
 		repo, _ := file.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {
@@ -67,16 +81,16 @@ func MysqlDDL(ctx *cli.Context) error {
 }
 
 // MySqlDataSource generates model code from datasource
-func MySqlDataSource(ctx *cli.Context) error {
-	migrationnotes.BeforeCommands(ctx)
-	url := strings.TrimSpace(ctx.String(flagURL))
-	dir := strings.TrimSpace(ctx.String(flagDir))
-	cache := ctx.Bool(flagCache)
-	idea := ctx.Bool(flagIdea)
-	style := ctx.String(flagStyle)
-	home := ctx.String(flagHome)
-	remote := ctx.String(flagRemote)
-	branch := ctx.String(flagBranch)
+func MySqlDataSource(_ *cobra.Command, _ []string) error {
+	migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
+	url := strings.TrimSpace(VarStringURL)
+	dir := strings.TrimSpace(VarStringDir)
+	cache := VarBoolCache
+	idea := VarBoolIdea
+	style := VarStringStyle
+	home := VarStringHome
+	remote := VarStringRemote
+	branch := VarStringBranch
 	if len(remote) > 0 {
 		repo, _ := file.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {
@@ -87,7 +101,7 @@ func MySqlDataSource(ctx *cli.Context) error {
 		pathx.RegisterGoctlHome(home)
 	}
 
-	tableValue := ctx.StringSlice(flagTable)
+	tableValue := VarStringSliceTable
 	patterns := parseTableList(tableValue)
 	cfg, err := config.NewConfig(style)
 	if err != nil {
@@ -135,17 +149,17 @@ func parseTableList(tableValue []string) pattern {
 }
 
 // PostgreSqlDataSource generates model code from datasource
-func PostgreSqlDataSource(ctx *cli.Context) error {
-	migrationnotes.BeforeCommands(ctx)
-	url := strings.TrimSpace(ctx.String(flagURL))
-	dir := strings.TrimSpace(ctx.String(flagDir))
-	cache := ctx.Bool(flagCache)
-	idea := ctx.Bool(flagIdea)
-	style := ctx.String(flagStyle)
-	schema := ctx.String(flagSchema)
-	home := ctx.String(flagHome)
-	remote := ctx.String(flagRemote)
-	branch := ctx.String(flagBranch)
+func PostgreSqlDataSource(_ *cobra.Command, _ []string) error {
+	migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
+	url := strings.TrimSpace(VarStringURL)
+	dir := strings.TrimSpace(VarStringDir)
+	cache := VarBoolCache
+	idea := VarBoolIdea
+	style := VarStringStyle
+	schema := VarStringSchema
+	home := VarStringHome
+	remote := VarStringRemote
+	branch := VarStringBranch
 	if len(remote) > 0 {
 		repo, _ := file.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {
@@ -160,7 +174,7 @@ func PostgreSqlDataSource(ctx *cli.Context) error {
 		schema = "public"
 	}
 
-	pattern := strings.TrimSpace(ctx.String(flagTable))
+	pattern := strings.TrimSpace(VarStringTable)
 	cfg, err := config.NewConfig(style)
 	if err != nil {
 		return err

+ 2 - 3
tools/goctl/model/sql/command/migrationnotes/migrationnotes.go

@@ -1,14 +1,13 @@
 package migrationnotes
 
 import (
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/config"
 	"github.com/zeromicro/go-zero/tools/goctl/util/format"
 )
 
 // BeforeCommands run before comamnd run to show some migration notes
-func BeforeCommands(ctx *cli.Context) error {
-	if err := migrateBefore1_3_4(ctx); err != nil {
+func BeforeCommands(dir, style string) error {
+	if err := migrateBefore1_3_4(dir, style); err != nil {
 		return err
 	}
 	return nil

+ 1 - 5
tools/goctl/model/sql/command/migrationnotes/v1.3.4.go

@@ -4,13 +4,9 @@ import (
 	"fmt"
 	"io/ioutil"
 	"strings"
-
-	"github.com/urfave/cli"
 )
 
-func migrateBefore1_3_4(ctx *cli.Context) error {
-	dir := ctx.String("dir")
-	style := ctx.String("style")
+func migrateBefore1_3_4(dir, style string) error {
 	ok, err := needShow1_3_4(dir, style)
 	if err != nil {
 		return err

+ 1 - 2
tools/goctl/model/sql/gen/template.go

@@ -3,7 +3,6 @@ package gen
 import (
 	"fmt"
 
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/template"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
@@ -70,7 +69,7 @@ func Clean() error {
 }
 
 // GenTemplates creates template files if not exists
-func GenTemplates(_ *cli.Context) error {
+func GenTemplates() error {
 	return pathx.InitTemplates(category, templates)
 }
 

+ 19 - 8
tools/goctl/plugin/plugin.go

@@ -13,7 +13,7 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/api/parser"
 	"github.com/zeromicro/go-zero/tools/goctl/api/spec"
 	"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
@@ -30,19 +30,30 @@ type Plugin struct {
 	Dir         string
 }
 
+var (
+	// VarStringPlugin describes a plugin.
+	VarStringPlugin string
+	// VarStringDir describes a directory.
+	VarStringDir string
+	// VarStringAPI describes an API file.
+	VarStringAPI string
+	// VarStringStyle describes a style.
+	VarStringStyle string
+)
+
 // PluginCommand is the entry of goctl api plugin
-func PluginCommand(c *cli.Context) error {
+func PluginCommand(_ *cobra.Command, _ []string) error {
 	ex, err := os.Executable()
 	if err != nil {
 		panic(err)
 	}
 
-	plugin := c.String("plugin")
+	plugin := VarStringPlugin
 	if len(plugin) == 0 {
 		return errors.New("missing plugin")
 	}
 
-	transferData, err := prepareArgs(c)
+	transferData, err := prepareArgs()
 	if err != nil {
 		return err
 	}
@@ -69,8 +80,8 @@ func PluginCommand(c *cli.Context) error {
 	return nil
 }
 
-func prepareArgs(c *cli.Context) ([]byte, error) {
-	apiPath := c.String("api")
+func prepareArgs() ([]byte, error) {
+	apiPath := VarStringAPI
 
 	var transferData Plugin
 	if len(apiPath) > 0 && pathx.FileExists(apiPath) {
@@ -88,13 +99,13 @@ func prepareArgs(c *cli.Context) ([]byte, error) {
 	}
 
 	transferData.ApiFilePath = absApiFilePath
-	dirAbs, err := filepath.Abs(c.String("dir"))
+	dirAbs, err := filepath.Abs(VarStringDir)
 	if err != nil {
 		return nil, err
 	}
 
 	transferData.Dir = dirAbs
-	transferData.Style = c.String("style")
+	transferData.Style = VarStringStyle
 	data, err := json.Marshal(transferData)
 	if err != nil {
 		return nil, err

+ 46 - 24
tools/goctl/rpc/cli/cli.go

@@ -6,30 +6,57 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/rpc/generator"
 	"github.com/zeromicro/go-zero/tools/goctl/util"
 	"github.com/zeromicro/go-zero/tools/goctl/util/console"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
+var (
+	// VarStringOutput describes the output.
+	VarStringOutput string
+	// VarStringHome describes the goctl home.
+	VarStringHome string
+	// VarStringRemote describes the remote git repository.
+	VarStringRemote string
+	// VarStringBranch describes the git branch.
+	VarStringBranch string
+	// VarStringSliceGoOut describes the go output.
+	VarStringSliceGoOut []string
+	// VarStringSliceGoGRPCOut describes the grpc output.
+	VarStringSliceGoGRPCOut []string
+	// VarStringSlicePlugin describes the protoc plugin.
+	VarStringSlicePlugin []string
+	// VarStringSliceProtoPath describes the proto path.
+	VarStringSliceProtoPath []string
+	// VarStringSliceGoOpt describes the go options.
+	VarStringSliceGoOpt []string
+	// VarStringSliceGoGRPCOpt describes the grpc options.
+	VarStringSliceGoGRPCOpt []string
+	// VarStringStyle describes the style of output files.
+	VarStringStyle string
+	// VarStringZRPCOut describes the zRPC output.
+	VarStringZRPCOut string
+	// VarBoolIdea describes whether idea or not
+	VarBoolIdea bool
+	// VarBoolVerbose describes whether verbose.
+	VarBoolVerbose bool
+)
+
 // RPCNew is to generate rpc greet service, this greet service can speed
 // up your understanding of the zrpc service structure
-func RPCNew(c *cli.Context) error {
-	if c.NArg() == 0 {
-		cli.ShowCommandHelpAndExit(c, "new", 1)
-	}
-
-	rpcname := c.Args().First()
+func RPCNew(_ *cobra.Command, args []string) error {
+	rpcname := args[0]
 	ext := filepath.Ext(rpcname)
 	if len(ext) > 0 {
 		return fmt.Errorf("unexpected ext: %s", ext)
 	}
-	style := c.String("style")
-	home := c.String("home")
-	remote := c.String("remote")
-	branch := c.String("branch")
-	verbose := c.Bool("verbose")
+	style := VarStringStyle
+	home := VarStringHome
+	remote := VarStringRemote
+	branch := VarStringBranch
+	verbose := VarBoolVerbose
 	if len(remote) > 0 {
 		repo, _ := util.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {
@@ -60,12 +87,12 @@ func RPCNew(c *cli.Context) error {
 	ctx.Output = filepath.Dir(src)
 	ctx.ProtocCmd = fmt.Sprintf("protoc -I=%s %s --go_out=%s --go-grpc_out=%s", filepath.Dir(src), filepath.Base(src), filepath.Dir(src), filepath.Dir(src))
 
-	grpcOptList := c.StringSlice("go-grpc_opt")
+	grpcOptList := VarStringSliceGoGRPCOpt
 	if len(grpcOptList) > 0 {
 		ctx.ProtocCmd += " --go-grpc_opt=" + strings.Join(grpcOptList, ",")
 	}
 
-	goOptList := c.StringSlice("go_opt")
+	goOptList := VarStringSliceGoOpt
 	if len(goOptList) > 0 {
 		ctx.ProtocCmd += " --go_opt=" + strings.Join(goOptList, ",")
 	}
@@ -75,17 +102,12 @@ func RPCNew(c *cli.Context) error {
 }
 
 // RPCTemplate is the entry for generate rpc template
-func RPCTemplate(c *cli.Context) error {
+func RPCTemplate(_ *cobra.Command, _ []string) error {
 	console.Warning("deprecated: goctl rpc template -o is deprecated and will be removed in the future, use goctl rpc -o instead")
-
-	if c.NumFlags() == 0 {
-		cli.ShowCommandHelpAndExit(c, "template", 1)
-	}
-
-	protoFile := c.String("o")
-	home := c.String("home")
-	remote := c.String("remote")
-	branch := c.String("branch")
+	protoFile := VarStringOutput
+	home := VarStringHome
+	remote := VarStringRemote
+	branch := VarStringBranch
 	if len(remote) > 0 {
 		repo, _ := util.CloneIntoGitHome(remote, branch)
 		if len(repo) > 0 {

+ 29 - 79
tools/goctl/rpc/cli/zrpc.go

@@ -6,7 +6,7 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/rpc/generator"
 	"github.com/zeromicro/go-zero/tools/goctl/util"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
@@ -16,37 +16,26 @@ var (
 	errInvalidGrpcOutput = errors.New("ZRPC: missing --go-grpc_out")
 	errInvalidGoOutput   = errors.New("ZRPC: missing --go_out")
 	errInvalidZrpcOutput = errors.New("ZRPC: missing zrpc output, please use --zrpc_out to specify the output")
-	errInvalidInput      = errors.New("ZRPC: missing source")
-	errMultiInput        = errors.New("ZRPC: only one source is expected")
 )
 
 // ZRPC generates grpc code directly by protoc and generates
 // zrpc code by goctl.
-func ZRPC(c *cli.Context) error {
-	if c.NumFlags() == 0 {
-		cli.ShowCommandHelpAndExit(c, "protoc", 1)
-	}
-
-	args := c.Parent().Args()
-	protocArgs := removeGoctlFlag(args)
+func ZRPC(cmd *cobra.Command, args []string) error {
+	protocArgs := wrapProtocCmd("protoc", args)
 	pwd, err := os.Getwd()
 	if err != nil {
 		return err
 	}
 
-	source, err := getSourceProto(c.Args(), pwd)
-	if err != nil {
-		return err
-	}
-
-	grpcOutList := c.StringSlice("go-grpc_out")
-	goOutList := c.StringSlice("go_out")
-	zrpcOut := c.String("zrpc_out")
-	style := c.String("style")
-	home := c.String("home")
-	remote := c.String("remote")
-	branch := c.String("branch")
-	verbose := c.Bool("verbose")
+	source := args[0]
+	grpcOutList := VarStringSliceGoGRPCOut
+	goOutList := VarStringSliceGoOut
+	zrpcOut := VarStringZRPCOut
+	style := VarStringStyle
+	home := VarStringHome
+	remote := VarStringRemote
+	branch := VarStringBranch
+	verbose := VarBoolVerbose
 	if len(grpcOutList) == 0 {
 		return errInvalidGrpcOutput
 	}
@@ -116,64 +105,25 @@ func ZRPC(c *cli.Context) error {
 	return g.Generate(&ctx)
 }
 
-func removeGoctlFlag(args []string) []string {
-	var ret []string
-	var step int
-	for step < len(args) {
-		arg := args[step]
-		switch {
-		case arg == "--style", arg == "--home",
-			arg == "--zrpc_out", arg == "--verbose",
-			arg == "-v", arg == "--remote",
-			arg == "--branch":
-			step += 2
-			continue
-		case strings.HasPrefix(arg, "--style="),
-			strings.HasPrefix(arg, "--home="),
-			strings.HasPrefix(arg, "--verbose="),
-			strings.HasPrefix(arg, "-v="),
-			strings.HasPrefix(arg, "--remote="),
-			strings.HasPrefix(arg, "--branch="),
-			strings.HasPrefix(arg, "--zrpc_out="):
-			step += 1
-			continue
-		}
-		step += 1
-		ret = append(ret, arg)
+func wrapProtocCmd(name string, args []string) []string {
+	ret := append([]string{name}, args...)
+	for _, protoPath := range VarStringSliceProtoPath {
+		ret = append(ret, "--proto_path", protoPath)
 	}
-
-	return ret
-}
-
-func getSourceProto(args []string, pwd string) (string, error) {
-	var source []string
-	for _, p := range args {
-		if strings.HasSuffix(p, ".proto") {
-			source = append(source, p)
-		}
+	for _, goOpt := range VarStringSliceGoOpt {
+		ret = append(ret, "--go_opt", goOpt)
 	}
-
-	switch len(source) {
-	case 0:
-		return "", errInvalidInput
-	case 1:
-		isAbs := filepath.IsAbs(source[0])
-		if isAbs {
-			return source[0], nil
-		}
-
-		abs := filepath.Join(pwd, source[0])
-		return abs, nil
-	default:
-		return "", errMultiInput
+	for _, goGRPCOpt := range VarStringSliceGoGRPCOpt {
+		ret = append(ret, "--go-grpc_opt", goGRPCOpt)
 	}
-}
-
-func removePluginFlag(goOut string) string {
-	goOut = strings.ReplaceAll(goOut, "plugins=", "")
-	index := strings.LastIndex(goOut, ":")
-	if index < 0 {
-		return goOut
+	for _, goOut := range VarStringSliceGoOut {
+		ret = append(ret, "--go_out", goOut)
+	}
+	for _, goGRPCOut := range VarStringSliceGoGRPCOut {
+		ret = append(ret, "--go-grpc_out", goGRPCOut)
 	}
-	return goOut[index+1:]
+	for _, plugin := range VarStringSlicePlugin {
+		ret = append(ret, "--plugin="+plugin)
+	}
+	return ret
 }

+ 0 - 125
tools/goctl/rpc/cli/zrpc_test.go

@@ -1,125 +0,0 @@
-package cli
-
-import (
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	"github.com/zeromicro/go-zero/tools/goctl/util/console"
-)
-
-type test struct {
-	source      []string
-	expected    string
-	expectedErr error
-}
-
-func Test_GetSourceProto(t *testing.T) {
-	pwd, err := os.Getwd()
-	if err != nil {
-		console.Error(err.Error())
-		return
-	}
-
-	testData := []test{
-		{
-			source:   []string{"a.proto"},
-			expected: filepath.Join(pwd, "a.proto"),
-		},
-		{
-			source:   []string{"/foo/bar/a.proto"},
-			expected: "/foo/bar/a.proto",
-		},
-		{
-			source:      []string{"a.proto", "b.proto"},
-			expectedErr: errMultiInput,
-		},
-		{
-			source:      []string{"", "--go_out=."},
-			expectedErr: errInvalidInput,
-		},
-	}
-
-	for _, d := range testData {
-		ret, err := getSourceProto(d.source, pwd)
-		if d.expectedErr != nil {
-			assert.Equal(t, d.expectedErr, err)
-			continue
-		}
-
-		assert.Equal(t, d.expected, ret)
-	}
-}
-
-func Test_RemoveGoctlFlag(t *testing.T) {
-	testData := []test{
-		{
-			source:   strings.Fields("protoc foo.proto --go_out=. --go_opt=bar --zrpc_out=. --style go-zero --home=foo"),
-			expected: "protoc foo.proto --go_out=. --go_opt=bar",
-		},
-		{
-			source:   strings.Fields("foo bar foo.proto"),
-			expected: "foo bar foo.proto",
-		},
-		{
-			source:   strings.Fields("protoc foo.proto --go_out . --style=go_zero --home ."),
-			expected: "protoc foo.proto --go_out .",
-		},
-		{
-			source:   strings.Fields(`protoc foo.proto --go_out . --style="go_zero" --home="."`),
-			expected: "protoc foo.proto --go_out .",
-		},
-		{
-			source:   strings.Fields(`protoc foo.proto --go_opt=. --zrpc_out . --style=goZero  --home=bar`),
-			expected: "protoc foo.proto --go_opt=.",
-		},
-		{
-			source:   strings.Fields(`protoc foo.proto --go_opt=. --zrpc_out="bar" --style=goZero  --home=bar`),
-			expected: "protoc foo.proto --go_opt=.",
-		},
-		{
-			source:   strings.Fields(`protoc --go_opt=. --go-grpc_out=. --zrpc_out=. foo.proto`),
-			expected: "protoc --go_opt=. --go-grpc_out=. foo.proto",
-		},
-		{
-			source:   strings.Fields(`protoc --go_opt=. --go-grpc_out=. --zrpc_out=. --remote=foo --branch=bar foo.proto`),
-			expected: "protoc --go_opt=. --go-grpc_out=. foo.proto",
-		},
-		{
-			source:   strings.Fields(`protoc --go_opt=. --go-grpc_out=. --zrpc_out=. --remote foo --branch bar foo.proto`),
-			expected: "protoc --go_opt=. --go-grpc_out=. foo.proto",
-		},
-	}
-	for _, e := range testData {
-		cmd := strings.Join(removeGoctlFlag(e.source), " ")
-		assert.Equal(t, e.expected, cmd)
-	}
-}
-
-func Test_RemovePluginFlag(t *testing.T) {
-	testData := []test{
-		{
-			source:   strings.Fields("plugins=grpc:."),
-			expected: ".",
-		},
-		{
-			source:   strings.Fields("plugins=g1,g2:."),
-			expected: ".",
-		},
-		{
-			source:   strings.Fields("g1,g2:."),
-			expected: ".",
-		},
-		{
-			source:   strings.Fields("plugins=g1,g2:foo"),
-			expected: "foo",
-		},
-	}
-
-	for _, e := range testData {
-		data := removePluginFlag(e.source[0])
-		assert.Equal(t, e.expected, data)
-	}
-}

+ 90 - 0
tools/goctl/rpc/cmd.go

@@ -0,0 +1,90 @@
+package rpc
+
+import (
+	"github.com/spf13/cobra"
+	"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:  cli.RPCTemplate,
+	}
+
+	newCmd = &cobra.Command{
+		Use:   "new",
+		Short: "Generate rpc demo service",
+		Args:  cobra.ExactValidArgs(1),
+		RunE:  cli.RPCNew,
+	}
+
+	templateCmd = &cobra.Command{
+		Use:   "template",
+		Short: "Generate proto template",
+		RunE:  cli.RPCTemplate,
+	}
+
+	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.ExactValidArgs(1),
+		RunE:    cli.ZRPC,
+	}
+)
+
+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")
+
+	newCmd.Flags().StringSliceVar(&cli.VarStringSliceGoOpt, "go_opt", nil, "")
+	newCmd.Flags().StringSliceVar(&cli.VarStringSliceGoGRPCOpt, "go-grpc_opt", nil, "")
+	newCmd.Flags().StringVar(&cli.VarStringStyle, "style", "gozero", "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")
+
+	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", "gozero", "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")
+
+	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")
+
+	Cmd.AddCommand(newCmd)
+	Cmd.AddCommand(protocCmd)
+	Cmd.AddCommand(templateCmd)
+}

+ 1 - 2
tools/goctl/rpc/generator/template.go

@@ -3,7 +3,6 @@ package generator
 import (
 	"fmt"
 
-	"github.com/urfave/cli"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
 )
 
@@ -40,7 +39,7 @@ var templates = map[string]string{
 
 // GenTemplates is the entry for command goctl template,
 // it will create the specified category
-func GenTemplates(_ *cli.Context) error {
+func GenTemplates() error {
 	return pathx.InitTemplates(category, templates)
 }
 

+ 55 - 0
tools/goctl/tpl/cmd.go

@@ -0,0 +1,55 @@
+package tpl
+
+import (
+	"github.com/spf13/cobra"
+)
+
+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,
+	}
+)
+
+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")
+
+	Cmd.AddCommand(cleanCmd)
+	Cmd.AddCommand(initCmd)
+	Cmd.AddCommand(revertCmd)
+	Cmd.AddCommand(updateCmd)
+}

+ 24 - 24
tools/goctl/tpl/templates.go

@@ -5,7 +5,7 @@ import (
 	"path/filepath"
 
 	"github.com/logrusorgru/aurora"
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/core/errorx"
 	"github.com/zeromicro/go-zero/tools/goctl/api/apigen"
 	"github.com/zeromicro/go-zero/tools/goctl/api/gogen"
@@ -20,37 +20,37 @@ import (
 
 const templateParentPath = "/"
 
-// GenTemplates writes the latest template text into file which is not exists
-func GenTemplates(ctx *cli.Context) error {
-	path := ctx.String("home")
+// genTemplates writes the latest template text into file which is not exists
+func genTemplates(_ *cobra.Command, _ []string) error {
+	path := varStringHome
 	if len(path) != 0 {
 		pathx.RegisterGoctlHome(path)
 	}
 
 	if err := errorx.Chain(
 		func() error {
-			return gogen.GenTemplates(ctx)
+			return gogen.GenTemplates()
 		},
 		func() error {
-			return modelgen.GenTemplates(ctx)
+			return modelgen.GenTemplates()
 		},
 		func() error {
-			return rpcgen.GenTemplates(ctx)
+			return rpcgen.GenTemplates()
 		},
 		func() error {
-			return docker.GenTemplates(ctx)
+			return docker.GenTemplates()
 		},
 		func() error {
-			return kube.GenTemplates(ctx)
+			return kube.GenTemplates()
 		},
 		func() error {
-			return mongogen.Templates(ctx)
+			return mongogen.Templates()
 		},
 		func() error {
-			return apigen.GenTemplates(ctx)
+			return apigen.GenTemplates()
 		},
 		func() error {
-			return apinew.GenTemplates(ctx)
+			return apinew.GenTemplates()
 		},
 	); err != nil {
 		return err
@@ -72,9 +72,9 @@ func GenTemplates(ctx *cli.Context) error {
 	return nil
 }
 
-// CleanTemplates deletes all templates
-func CleanTemplates(ctx *cli.Context) error {
-	path := ctx.String("home")
+// cleanTemplates deletes all templates
+func cleanTemplates(_ *cobra.Command, _ []string) error {
+	path := varStringHome
 	if len(path) != 0 {
 		pathx.RegisterGoctlHome(path)
 	}
@@ -113,11 +113,11 @@ func CleanTemplates(ctx *cli.Context) error {
 	return nil
 }
 
-// UpdateTemplates writes the latest template text into file,
+// updateTemplates writes the latest template text into file,
 // it will delete the older templates if there are exists
-func UpdateTemplates(ctx *cli.Context) (err error) {
-	path := ctx.String("home")
-	category := ctx.String("category")
+func updateTemplates(_ *cobra.Command, _ []string) (err error) {
+	path := varStringHome
+	category := varStringCategory
 	if len(path) != 0 {
 		pathx.RegisterGoctlHome(path)
 	}
@@ -150,11 +150,11 @@ func UpdateTemplates(ctx *cli.Context) (err error) {
 	}
 }
 
-// RevertTemplates will overwrite the old template content with the new template
-func RevertTemplates(ctx *cli.Context) (err error) {
-	path := ctx.String("home")
-	category := ctx.String("category")
-	filename := ctx.String("name")
+// revertTemplates will overwrite the old template content with the new template
+func revertTemplates(_ *cobra.Command, _ []string) (err error) {
+	path := varStringHome
+	category := varStringCategory
+	filename := varStringName
 	if len(path) != 0 {
 		pathx.RegisterGoctlHome(path)
 	}

+ 10 - 0
tools/goctl/upgrade/cmd.go

@@ -0,0 +1,10 @@
+package upgrade
+
+import "github.com/spf13/cobra"
+
+// Cmd describes a upgrade command.
+var Cmd = &cobra.Command{
+	Use:   "upgrade",
+	Short: "Upgrade goctl to latest version",
+	RunE:  upgrade,
+}

+ 2 - 2
tools/goctl/upgrade/upgrade.go

@@ -4,13 +4,13 @@ import (
 	"fmt"
 	"runtime"
 
-	"github.com/urfave/cli"
+	"github.com/spf13/cobra"
 	"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
 )
 
 // Upgrade gets the latest goctl by
 // go install github.com/zeromicro/go-zero/tools/goctl@latest
-func Upgrade(_ *cli.Context) error {
+func upgrade(_ *cobra.Command, _ []string) error {
 	cmd := `GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest`
 	if runtime.GOOS == "windows" {
 		cmd = `set GOPROXY=https://goproxy.cn,direct && go install github.com/zeromicro/go-zero/tools/goctl@latest`

+ 18 - 0
tools/goctl/util/pathx/file.go

@@ -2,6 +2,8 @@ package pathx
 
 import (
 	"bufio"
+	"crypto/md5"
+	"encoding/hex"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -283,3 +285,19 @@ func Copy(src, dest string) error {
 	_, err = io.Copy(w, f)
 	return err
 }
+
+func Hash(file string) (string, error) {
+	f, err := os.Open(file)
+	if err != nil {
+		return "", err
+	}
+	defer func() {
+		_ = f.Close()
+	}()
+	hash := md5.New()
+	_, err = io.Copy(hash, f)
+	if err != nil {
+		return "", err
+	}
+	return hex.EncodeToString(hash.Sum(nil)), nil
+}