feat: Add goctl quickstart (#1889)
* Add goctl quickstart * Format code * Format code
This commit is contained in:
25
tools/goctl/quickstart/cmd.go
Normal file
25
tools/goctl/quickstart/cmd.go
Normal 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]")
|
||||
}
|
||||
3
tools/goctl/quickstart/idl/api.yaml
Normal file
3
tools/goctl/quickstart/idl/api.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
Name: ping
|
||||
Host: 127.0.0.1
|
||||
Port: 8888
|
||||
35
tools/goctl/quickstart/idl/apilogic.tpl
Normal file
35
tools/goctl/quickstart/idl/apilogic.tpl
Normal 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
|
||||
}
|
||||
10
tools/goctl/quickstart/idl/greet.api
Normal file
10
tools/goctl/quickstart/idl/greet.api
Normal file
@@ -0,0 +1,10 @@
|
||||
syntax = "v1"
|
||||
|
||||
type resp {
|
||||
msg string `json:"msg"`
|
||||
}
|
||||
|
||||
service greet {
|
||||
@handler ping
|
||||
post /ping returns (resp)
|
||||
}
|
||||
10
tools/goctl/quickstart/idl/greet.proto
Normal file
10
tools/goctl/quickstart/idl/greet.proto
Normal file
@@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ping;
|
||||
option go_package = "./pb";
|
||||
|
||||
message Placeholder{}
|
||||
|
||||
service Greet {
|
||||
rpc ping (Placeholder) returns (Placeholder);
|
||||
}
|
||||
22
tools/goctl/quickstart/idl/svc.tpl
Normal file
22
tools/goctl/quickstart/idl/svc.tpl
Normal 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),
|
||||
}
|
||||
}
|
||||
77
tools/goctl/quickstart/micro.go
Normal file
77
tools/goctl/quickstart/micro.go
Normal 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()
|
||||
}
|
||||
79
tools/goctl/quickstart/mono.go
Normal file
79
tools/goctl/quickstart/mono.go
Normal 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)
|
||||
}
|
||||
79
tools/goctl/quickstart/quickstart.go
Normal file
79
tools/goctl/quickstart/quickstart.go
Normal 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
|
||||
}
|
||||
35
tools/goctl/quickstart/run.go
Normal file
35
tools/goctl/quickstart/run.go
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user