feat: Add goctl quickstart (#1889)

* Add goctl quickstart

* Format code

* Format code
This commit is contained in:
anqiansong
2022-05-13 12:23:24 +08:00
committed by GitHub
parent f486685e99
commit 52f060caae
13 changed files with 395 additions and 2 deletions

View File

@@ -0,0 +1,25 @@
package quickstart
import "github.com/spf13/cobra"
const (
serviceTypeMono = "mono"
serviceTypeMicro = "micro"
)
var (
varStringServiceType string
// Cmd describes the command to run.
Cmd = &cobra.Command{
Use: "quickstart",
Short: "quickly start a project",
RunE: run,
}
)
func init() {
Cmd.Flags().StringVarP(&varStringServiceType,
"service-type", "t", "mono",
"specify the service type, supported values: [mono, micro]")
}

View File

@@ -0,0 +1,3 @@
Name: ping
Host: 127.0.0.1
Port: 8888

View File

@@ -0,0 +1,35 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"greet/api/internal/svc"
"greet/api/internal/types"
{{if .callRPC}}"greet/rpc/greet"
{{end}}
)
type PingLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewPingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PingLogic {
return &PingLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *PingLogic) Ping() (resp *types.Resp, err error) {
{{if .callRPC}}if _, err = l.svcCtx.GreetRpc.Ping(l.ctx, &greet.Placeholder{});err !=nil {
return
}{{end}}
resp = new(types.Resp)
resp.Msg = "pong"
return
}

View File

@@ -0,0 +1,10 @@
syntax = "v1"
type resp {
msg string `json:"msg"`
}
service greet {
@handler ping
post /ping returns (resp)
}

View File

@@ -0,0 +1,10 @@
syntax = "proto3";
package ping;
option go_package = "./pb";
message Placeholder{}
service Greet {
rpc ping (Placeholder) returns (Placeholder);
}

View File

@@ -0,0 +1,22 @@
package svc
import (
"github.com/zeromicro/go-zero/zrpc"
"greet/api/internal/config"
"greet/rpc/greet"
)
type ServiceContext struct {
Config config.Config
GreetRpc greet.Greet
}
func NewServiceContext(c config.Config) *ServiceContext {
client := zrpc.MustNewClient(zrpc.RpcClientConf{
Target: "127.0.0.1:8080",
})
return &ServiceContext{
Config: c,
GreetRpc: greet.NewGreet(client),
}
}

View File

@@ -0,0 +1,77 @@
package quickstart
import (
_ "embed"
"io/ioutil"
"path/filepath"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/service"
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)
const (
rpcEtcContent = `Name: greet.rpc
ListenOn: 127.0.0.1:8080
`
protoName = "greet.proto"
)
var (
//go:embed idl/greet.proto
protocContent string
zRPCWorkDir string
)
type serviceImpl struct {
starter func()
}
func (s serviceImpl) Start() {
s.starter()
}
func (s serviceImpl) Stop() {}
func initRPCProto() error {
zRPCWorkDir = filepath.Join(projectDir, "rpc")
if err := pathx.MkdirIfNotExist(zRPCWorkDir); err != nil {
return err
}
protoFilename := filepath.Join(zRPCWorkDir, protoName)
rpcBytes := []byte(protocContent)
return ioutil.WriteFile(protoFilename, rpcBytes, 0666)
}
type micro struct{}
func newMicroService() micro {
m := micro{}
m.mustStartRPCProject()
return m
}
func (m micro) mustStartRPCProject() {
logx.Must(initRPCProto())
log.Debug(">> Generating quickstart zRPC project...")
arg := "goctl rpc protoc " + protoName + " --go_out=. --go-grpc_out=. --zrpc_out=. --verbose"
execCommand(zRPCWorkDir, arg)
etcFile := filepath.Join(zRPCWorkDir, "etc", "greet.yaml")
logx.Must(ioutil.WriteFile(etcFile, []byte(rpcEtcContent), 0666))
}
func (m micro) start() {
mono := newMonoService(true)
goModTidy(projectDir)
sg := service.NewServiceGroup()
sg.Add(serviceImpl{func() {
log.Debug(">> Ready to start an zRPC server...")
goStart(zRPCWorkDir)
}})
sg.Add(serviceImpl{func() {
mono.start()
}})
sg.Start()
}

View File

@@ -0,0 +1,79 @@
package quickstart
import (
_ "embed"
"io/ioutil"
"path/filepath"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/tools/goctl/api/gogen"
"github.com/zeromicro/go-zero/tools/goctl/util"
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)
var (
//go:embed idl/greet.api
apiContent string
//go:embed idl/svc.tpl
svcContent string
//go:embed idl/apilogic.tpl
apiLogicContent string
//go:embed idl/api.yaml
apiEtcContent string
apiWorkDir string
)
func initAPIFlags() error {
apiWorkDir = filepath.Join(projectDir, "api")
if err := pathx.MkdirIfNotExist(apiWorkDir); err != nil {
return err
}
apiFilename := filepath.Join(apiWorkDir, "greet.api")
apiBytes := []byte(apiContent)
if err := ioutil.WriteFile(apiFilename, apiBytes, 0666); err != nil {
return err
}
gogen.VarStringDir = apiWorkDir
gogen.VarStringAPI = apiFilename
return nil
}
type mono struct {
callRPC bool
}
func newMonoService(callRPC bool) mono {
m := mono{callRPC}
m.createAPIProject()
return m
}
func (m mono) createAPIProject() {
logx.Must(initAPIFlags())
log.Debug(">> Generating quickstart api project...")
logx.Must(gogen.GoCommand(nil, nil))
etcFile := filepath.Join(apiWorkDir, "etc", "greet.yaml")
logx.Must(ioutil.WriteFile(etcFile, []byte(apiEtcContent), 0666))
logicFile := filepath.Join(apiWorkDir, "internal", "logic", "pinglogic.go")
logx.Must(util.With("logic").Parse(apiLogicContent).SaveTo(map[string]bool{
"callRPC": m.callRPC,
}, logicFile, true))
if m.callRPC {
svcFile := filepath.Join(apiWorkDir, "internal", "svc", "servicecontext.go")
logx.Must(ioutil.WriteFile(svcFile, []byte(svcContent), 0666))
}
}
func (m mono) start() {
if !m.callRPC {
goModTidy(projectDir)
}
log.Debug(">> Ready to start an api server...")
log.Debug(">> Try to execute 'curl --request POST http://127.0.0.1:8888/ping' after service startup...")
goStart(apiWorkDir)
}

View File

@@ -0,0 +1,79 @@
package quickstart
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/cobra"
"github.com/zeromicro/go-zero/core/logx"
"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/util/pathx"
)
const baseDir = "greet"
var (
log = console.NewColorConsole(true)
projectDir string
)
func cleanWorkSpace(projectDir string) {
var command string
var breakeState bool
fmt.Printf("Detected that the %q already exists, do you clean up?"+
" [y: YES, n: NO]: ", projectDir)
for {
fmt.Scanln(&command)
switch command {
case "y":
log.Debug("Clean workspace...")
_ = os.RemoveAll(projectDir)
breakeState = true
break
case "n":
log.Error("User canceled")
os.Exit(1)
default:
fmt.Println("Invalid command, try again...")
}
if breakeState {
break
}
}
}
func initProject() {
wd, err := os.Getwd()
logx.Must(err)
projectDir = filepath.Join(wd, baseDir)
if exists := pathx.FileExists(projectDir); exists {
cleanWorkSpace(projectDir)
}
log.Must(pathx.MkdirIfNotExist(projectDir))
if hasGoMod, _ := ctx.IsGoMod(projectDir); hasGoMod {
return
}
if exitCode := execCommand(projectDir, "go mod init "+baseDir); exitCode != 0 {
log.Fatalln("Init process exit")
}
}
func run(_ *cobra.Command, _ []string) error {
initProject()
switch varStringServiceType {
case serviceTypeMono:
newMonoService(false).start()
case serviceTypeMicro:
newMicroService().start()
default:
return fmt.Errorf("invalid service type, expected %s | %s",
serviceTypeMono, serviceTypeMicro)
}
return nil
}

View File

@@ -0,0 +1,35 @@
package quickstart
import (
"os"
"os/exec"
"runtime"
"github.com/zeromicro/go-zero/tools/goctl/vars"
)
func goStart(dir string) {
goproxy := "GOPROXY=https://goproxy.cn"
execCommand(dir, "go run .", goproxy)
}
func goModTidy(dir string) int {
goproxy := "GOPROXY=https://goproxy.cn"
log.Debug(">> go mod tidy")
return execCommand(dir, "go mod tidy", goproxy)
}
func execCommand(dir string, arg string, envArgs ...string) int {
cmd := exec.Command("sh", "-c", arg)
if runtime.GOOS == vars.OsWindows {
cmd = exec.Command("cmd.exe", "/c", arg)
}
env := append([]string(nil), os.Environ()...)
env = append(env, envArgs...)
cmd.Env = env
cmd.Dir = dir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
_ = cmd.Run()
return cmd.ProcessState.ExitCode()
}