From 7cbb0574b9813b21d01ac8e3c1e5499e002e8726 Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Wed, 24 Feb 2021 19:17:27 +0800 Subject: [PATCH] add proto client/server (#719) * add proto client/server --- README.md | 21 +++-- cmd/kratos/internal/base/get.go | 19 +++++ cmd/kratos/internal/base/mod.go | 40 +++++++++ cmd/kratos/internal/base/mod_test.go | 11 +++ cmd/kratos/internal/base/repo_test.go | 4 +- .../{new/project.go => project/new.go} | 6 +- .../{new/new.go => project/project.go} | 4 +- cmd/kratos/internal/proto/client/client.go | 83 +++++++++++++++++++ cmd/kratos/internal/proto/proto.go | 8 +- .../{service/service.go => server/server.go} | 34 ++++---- .../proto/{service => server}/template.go | 2 +- cmd/kratos/internal/proto/source/source.go | 28 ------- cmd/kratos/internal/upgrade/upgrade.go | 30 +++++++ cmd/kratos/main.go | 23 +++-- cmd/kratos/version.go | 13 --- 15 files changed, 238 insertions(+), 88 deletions(-) create mode 100644 cmd/kratos/internal/base/get.go create mode 100644 cmd/kratos/internal/base/mod_test.go rename cmd/kratos/internal/{new/project.go => project/new.go} (84%) rename cmd/kratos/internal/{new/new.go => project/project.go} (92%) create mode 100644 cmd/kratos/internal/proto/client/client.go rename cmd/kratos/internal/proto/{service/service.go => server/server.go} (61%) rename cmd/kratos/internal/proto/{service => server}/template.go (98%) delete mode 100644 cmd/kratos/internal/proto/source/source.go create mode 100644 cmd/kratos/internal/upgrade/upgrade.go delete mode 100644 cmd/kratos/version.go diff --git a/README.md b/README.md index c13a11f7f..dd95676cb 100644 --- a/README.md +++ b/README.md @@ -58,17 +58,16 @@ kratos new helloworld cd helloworld # 生成proto模板 kratos proto add api/helloworld/helloworld.proto -# 生成service模板 -kratos proto service api/helloworld/helloworld.proto -t internal/service - -# 安装生成工具 -make init -# 生成api下所有proto文件 -make proto -# 编译cmd下所有main文件 -make build -# 进行单元测试 -make test +# 生成proto源码 +kratos proto client api/helloworld/helloworld.proto +# 生成server模板 +kratos proto server api/helloworld/helloworld.proto -t internal/service + +# 编译成可执行文件 +cd /cmd/helloworld +go build +# 运行程序 +./helloword ``` ### Kratos Boot diff --git a/cmd/kratos/internal/base/get.go b/cmd/kratos/internal/base/get.go new file mode 100644 index 000000000..00c58c1ee --- /dev/null +++ b/cmd/kratos/internal/base/get.go @@ -0,0 +1,19 @@ +package base + +import ( + "os" + "os/exec" +) + +// GoGet go get path. +func GoGet(path ...string) error { + for _, p := range path { + cmd := exec.Command("go", "get", "-u", p) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + } + return nil +} diff --git a/cmd/kratos/internal/base/mod.go b/cmd/kratos/internal/base/mod.go index 4df815018..fd0f6852a 100644 --- a/cmd/kratos/internal/base/mod.go +++ b/cmd/kratos/internal/base/mod.go @@ -1,7 +1,13 @@ package base import ( + "bufio" + "bytes" "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" "golang.org/x/mod/modfile" ) @@ -14,3 +20,37 @@ func ModulePath(filename string) (string, error) { } return modfile.ModulePath(modBytes), nil } + +// ModuleVersion returns module version. +func ModuleVersion(path string) (string, error) { + stdout := &bytes.Buffer{} + fd := exec.Command("go", "mod", "graph") + fd.Stdout = stdout + fd.Stderr = stdout + if err := fd.Run(); err != nil { + return "", err + } + rd := bufio.NewReader(stdout) + for { + line, _, err := rd.ReadLine() + if err != nil { + return "", err + } + str := string(line) + i := strings.Index(str, "@") + if strings.Contains(str, path+"@") && i != -1 { + return path + str[i:], nil + } + } +} + +// KratosMod returns kratos mod. +func KratosMod() string { + gopath := os.Getenv("GOPATH") + if path, err := ModuleVersion("github.com/go-kratos/kratos/v2"); err == nil { + // $GOPATH/pkg/mod/github.com/go-kratos/kratos@v2 + return filepath.Join(gopath, "pkg", "mod", path) + } + // $GOPATH/src/github.com/go-kratos/kratos + return filepath.Join(gopath, "src", "github.com", "go-kratos", "kratos") +} diff --git a/cmd/kratos/internal/base/mod_test.go b/cmd/kratos/internal/base/mod_test.go new file mode 100644 index 000000000..9ce7e3ccb --- /dev/null +++ b/cmd/kratos/internal/base/mod_test.go @@ -0,0 +1,11 @@ +package base + +import "testing" + +func TestModuleVersion(t *testing.T) { + v, err := ModuleVersion("golang.org/x/mod") + if err != nil { + t.Fatal(err) + } + t.Log(v) +} diff --git a/cmd/kratos/internal/base/repo_test.go b/cmd/kratos/internal/base/repo_test.go index 69fa17c35..3ab488163 100644 --- a/cmd/kratos/internal/base/repo_test.go +++ b/cmd/kratos/internal/base/repo_test.go @@ -6,11 +6,11 @@ import ( ) func TestRepo(t *testing.T) { - r := NewRepo(RepoURL("https://github.com/go-kratos/service-layout.git")) + r := NewRepo("https://github.com/go-kratos/service-layout.git") if err := r.Clone(context.Background()); err != nil { t.Fatal(err) } - if err := r.CopyTo(context.Background(), "/tmp/test_repo"); err != nil { + if err := r.CopyTo(context.Background(), "/tmp/test_repo", "github.com/go-kratos/kratos-layout", nil); err != nil { t.Fatal(err) } } diff --git a/cmd/kratos/internal/new/project.go b/cmd/kratos/internal/project/new.go similarity index 84% rename from cmd/kratos/internal/new/project.go rename to cmd/kratos/internal/project/new.go index ffb95e2b3..9a6720c0e 100644 --- a/cmd/kratos/internal/new/project.go +++ b/cmd/kratos/internal/project/new.go @@ -1,4 +1,4 @@ -package new +package project import ( "context" @@ -18,8 +18,8 @@ type Project struct { Name string } -// Generate generate template project. -func (p *Project) Generate(ctx context.Context, dir string) error { +// New new a project from remote repo. +func (p *Project) New(ctx context.Context, dir string) error { to := path.Join(dir, p.Name) if _, err := os.Stat(to); !os.IsNotExist(err) { return fmt.Errorf("%s already exists", p.Name) diff --git a/cmd/kratos/internal/new/new.go b/cmd/kratos/internal/project/project.go similarity index 92% rename from cmd/kratos/internal/new/new.go rename to cmd/kratos/internal/project/project.go index 3893c40b1..8bb4c791e 100644 --- a/cmd/kratos/internal/new/new.go +++ b/cmd/kratos/internal/project/project.go @@ -1,4 +1,4 @@ -package new +package project import ( "context" @@ -29,7 +29,7 @@ func run(cmd *cobra.Command, args []string) { return } p := &Project{Name: args[0]} - if err := p.Generate(ctx, wd); err != nil { + if err := p.New(ctx, wd); err != nil { fmt.Fprintf(os.Stderr, "\033[31mERROR: %s\033[m\n", err) return } diff --git a/cmd/kratos/internal/proto/client/client.go b/cmd/kratos/internal/proto/client/client.go new file mode 100644 index 000000000..1bee3508d --- /dev/null +++ b/cmd/kratos/internal/proto/client/client.go @@ -0,0 +1,83 @@ +package client + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/go-kratos/kratos/cmd/kratos/v2/internal/base" + "github.com/spf13/cobra" +) + +var ( + // CmdClient represents the source command. + CmdClient = &cobra.Command{ + Use: "client", + Short: "Generate the proto client code", + Long: "Generate the proto client code. Example: kratos proto client helloworld.proto", + Run: run, + } +) + +func run(cmd *cobra.Command, args []string) { + if len(args) == 0 { + fmt.Println("Please enter the proto file or directory") + return + } + var ( + err error + proto = strings.TrimSpace(args[0]) + ) + if _, err = exec.LookPath("protoc-gen-go-http"); err != nil { + // update the kratos plugins + if err := exec.Command("kratos", "upgrade").Run(); err != nil { + fmt.Println(err) + } + } + if strings.HasSuffix(proto, ".proto") { + err = generate(proto) + } else { + err = walk(proto) + } + if err != nil { + fmt.Println(err) + } +} + +func walk(dir string) error { + if dir == "" { + dir = "." + } + return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if ext := filepath.Ext(path); ext != ".proto" { + return nil + } + return generate(path) + }) +} + +// generate is used to execute the generate command for the specified proto file +func generate(proto string) error { + path, name := filepath.Split(proto) + fd := exec.Command("protoc", []string{ + "--proto_path=.", + "--proto_path=" + filepath.Join(base.KratosMod(), "api"), + "--proto_path=" + filepath.Join(base.KratosMod(), "third_party"), + "--proto_path=" + filepath.Join(os.Getenv("GOPATH"), "src"), + "--go_out=paths=source_relative:.", + "--go-grpc_out=paths=source_relative:.", + "--go-http_out=paths=source_relative:.", + "--go-errors_out=paths=source_relative:.", + name, + }...) + fd.Stdout = os.Stdout + fd.Stderr = os.Stderr + fd.Dir = path + if err := fd.Run(); err != nil { + return err + } + fmt.Printf("proto: %s\n", proto) + return nil +} diff --git a/cmd/kratos/internal/proto/proto.go b/cmd/kratos/internal/proto/proto.go index baab7a480..87a1de9ab 100644 --- a/cmd/kratos/internal/proto/proto.go +++ b/cmd/kratos/internal/proto/proto.go @@ -2,8 +2,8 @@ package proto import ( "github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/add" - "github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/service" - "github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/source" + "github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/client" + "github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto/server" "github.com/spf13/cobra" ) @@ -18,8 +18,8 @@ var CmdProto = &cobra.Command{ func init() { CmdProto.AddCommand(add.CmdAdd) - CmdProto.AddCommand(source.CmdSource) - CmdProto.AddCommand(service.CmdService) + CmdProto.AddCommand(client.CmdClient) + CmdProto.AddCommand(server.CmdServer) } func run(cmd *cobra.Command, args []string) { diff --git a/cmd/kratos/internal/proto/service/service.go b/cmd/kratos/internal/proto/server/server.go similarity index 61% rename from cmd/kratos/internal/proto/service/service.go rename to cmd/kratos/internal/proto/server/server.go index a2dca6276..5ca3250c4 100644 --- a/cmd/kratos/internal/proto/service/service.go +++ b/cmd/kratos/internal/proto/server/server.go @@ -1,4 +1,4 @@ -package service +package server import ( "fmt" @@ -12,22 +12,22 @@ import ( "github.com/spf13/cobra" ) -// CmdService represents the service command. -var CmdService = &cobra.Command{ - Use: "service", - Short: "Generate the proto Service implementations", - Long: "Generate the proto Service implementations. Example: kratos proto service api/xxx.proto -target-dir=internal/service", +// CmdServer the service command. +var CmdServer = &cobra.Command{ + Use: "server", + Short: "Generate the proto Server implementations", + Long: "Generate the proto Server implementations. Example: kratos proto server api/xxx.proto -target-dir=internal/service", Run: run, } var targetDir string func init() { - CmdService.Flags().StringVarP(&targetDir, "-target-dir", "t", "internal/service", "generate target directory") + CmdServer.Flags().StringVarP(&targetDir, "-target-dir", "t", "internal/service", "generate target directory") } func run(cmd *cobra.Command, args []string) { if len(args) == 0 { - fmt.Fprintln(os.Stderr, "Please specify the proto file. Example: kratos proto service api/xxx.proto") + fmt.Fprintln(os.Stderr, "Please specify the proto file. Example: kratos proto server api/xxx.proto") return } reader, err := os.Open(args[0]) @@ -66,21 +66,23 @@ func run(cmd *cobra.Command, args []string) { res = append(res, cs) }), ) + if _, err := os.Stat(targetDir); os.IsNotExist(err) { + fmt.Printf("Target directory: %s does not exsits\n", targetDir) + return + } for _, s := range res { to := path.Join(targetDir, strings.ToLower(s.Service)+".go") - _, err := os.Stat(to) - if !os.IsNotExist(err) { - fmt.Fprintf(os.Stderr, "%s already exists\n", s.Service) - continue - } - if err = os.MkdirAll(targetDir, os.ModeDir); err != nil { - fmt.Fprintf(os.Stderr, "Failed to create file directory: %s\n", targetDir) + if _, err := os.Stat(to); !os.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "%s already exists: %s\n", s.Service, to) continue } b, err := s.execute() if err != nil { log.Fatal(err) } - ioutil.WriteFile(to, b, 0644) + if err := ioutil.WriteFile(to, b, 0644); err != nil { + log.Fatal(err) + } + fmt.Println(to) } } diff --git a/cmd/kratos/internal/proto/service/template.go b/cmd/kratos/internal/proto/server/template.go similarity index 98% rename from cmd/kratos/internal/proto/service/template.go rename to cmd/kratos/internal/proto/server/template.go index b2f255187..e30433a62 100644 --- a/cmd/kratos/internal/proto/service/template.go +++ b/cmd/kratos/internal/proto/server/template.go @@ -1,4 +1,4 @@ -package service +package server import ( "bytes" diff --git a/cmd/kratos/internal/proto/source/source.go b/cmd/kratos/internal/proto/source/source.go deleted file mode 100644 index 799056de5..000000000 --- a/cmd/kratos/internal/proto/source/source.go +++ /dev/null @@ -1,28 +0,0 @@ -package source - -import ( - "fmt" - "log" - "os/exec" - - "github.com/spf13/cobra" -) - -// CmdSource represents the source command. -var CmdSource = &cobra.Command{ - Use: "source", - Short: "Generate the proto source code", - Long: "Generate the proto source code. Example: kratos proto source ./**/*.proto", - Run: run, -} - -func run(cmd *cobra.Command, args []string) { - input := []string{"--go_out=paths=source_relative:.", "--go-grpc_out=paths=source_relative:."} - input = append(input, args...) - do := exec.Command("protoc", input...) - out, err := do.CombinedOutput() - if err != nil { - log.Fatalf("failed to execute: %s\n", err) - } - fmt.Println(string(out)) -} diff --git a/cmd/kratos/internal/upgrade/upgrade.go b/cmd/kratos/internal/upgrade/upgrade.go new file mode 100644 index 000000000..382899bcd --- /dev/null +++ b/cmd/kratos/internal/upgrade/upgrade.go @@ -0,0 +1,30 @@ +package upgrade + +import ( + "fmt" + + "github.com/go-kratos/kratos/cmd/kratos/v2/internal/base" + "github.com/spf13/cobra" +) + +// CmdUpgrade represents the upgrade command. +var CmdUpgrade = &cobra.Command{ + Use: "upgrade", + Short: "Upgrade the kratos tools", + Long: "Upgrade the kratos tools. Example: kratos upgrade", + Run: Run, +} + +// Run upgrade the kratos tools. +func Run(cmd *cobra.Command, args []string) { + err := base.GoGet( + "github.com/go-kratos/kratos/cmd/kratos/v2", + "github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2", + "github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2", + "google.golang.org/protobuf/cmd/protoc-gen-go", + "google.golang.org/grpc/cmd/protoc-gen-go-grpc", + ) + if err != nil { + fmt.Println(err) + } +} diff --git a/cmd/kratos/main.go b/cmd/kratos/main.go index dea806707..19150b815 100644 --- a/cmd/kratos/main.go +++ b/cmd/kratos/main.go @@ -3,21 +3,28 @@ package main import ( "log" - "github.com/go-kratos/kratos/cmd/kratos/v2/internal/new" + "github.com/go-kratos/kratos/cmd/kratos/v2/internal/project" "github.com/go-kratos/kratos/cmd/kratos/v2/internal/proto" + "github.com/go-kratos/kratos/cmd/kratos/v2/internal/upgrade" "github.com/spf13/cobra" ) -var rootCmd = &cobra.Command{ - Use: "kratos", - Short: "Kratos: An elegant toolkit for Go microservices.", - Long: `Kratos: An elegant toolkit for Go microservices.`, - Version: Version, -} +var ( + // Version is the version of the compiled software. + Version string = "v2.0.0-alpha4" + + rootCmd = &cobra.Command{ + Use: "kratos", + Short: "Kratos: An elegant toolkit for Go microservices.", + Long: `Kratos: An elegant toolkit for Go microservices.`, + Version: Version, + } +) func init() { - rootCmd.AddCommand(new.CmdNew) + rootCmd.AddCommand(project.CmdNew) rootCmd.AddCommand(proto.CmdProto) + rootCmd.AddCommand(upgrade.CmdUpgrade) } func main() { diff --git a/cmd/kratos/version.go b/cmd/kratos/version.go deleted file mode 100644 index 8d657404f..000000000 --- a/cmd/kratos/version.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -// go build -ldflags "-X main.Version=x.y.yz" -var ( - // Version is the version of the compiled software. - Version string = "v2.0.0" - // Branch is current branch name the code is built off - Branch string - // Revision is the short commit hash of source tree - Revision string - // BuildDate is the date when the binary was built. - BuildDate string -)