add kratos tool (#4)

pull/5/head
Felix Hao 6 years ago committed by GitHub
parent d0b98db00c
commit 7fc7de272c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 4
      go.mod
  3. 55
      go.sum
  4. 14
      tool/kratos/README.MD
  5. 22
      tool/kratos/build.go
  6. 27
      tool/kratos/doc.go
  7. 155
      tool/kratos/init.go
  8. 72
      tool/kratos/main.go
  9. 174
      tool/kratos/project.go
  10. 451
      tool/kratos/template.go
  11. 175
      tool/kratos/tool.go
  12. 15
      tool/kratos/tool_index.go
  13. 44
      tool/kratos/version.go

1
.gitignore vendored

@ -0,0 +1 @@
go.sum

@ -1,15 +1,19 @@
module github.com/bilibili/Kratos
require (
github.com/fatih/color v1.7.0
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/gogo/protobuf v1.2.0
github.com/golang/protobuf v1.2.0
github.com/kr/pty v1.1.4
github.com/leodido/go-urn v1.1.0 // indirect
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.2
github.com/stretchr/testify v1.3.0
github.com/urfave/cli v1.20.0
google.golang.org/grpc v1.18.0
gopkg.in/AlecAivazis/survey.v1 v1.8.2
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.26.0
)

@ -1,55 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA=
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.26.0 h1:2NPPsBpD0ZoxshmLWewQru8rWmbT5JqSzz9D1ZrAjYQ=
gopkg.in/go-playground/validator.v9 v9.26.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -0,0 +1,14 @@
# kratos
## 项目简介
Kratos 工具
## 安装
`go get -u github.com/bilibili/Kratos/tool/kratos`
## 使用说明
### 参数
kratos -h

@ -0,0 +1,22 @@
package main
import (
"io"
"os"
"os/exec"
"github.com/kr/pty"
"github.com/urfave/cli"
)
func buildAction(c *cli.Context) error {
args := append([]string{"build"}, c.Args()...)
// fmt.Println(args)
cmd := exec.Command("go", args...)
f, err := pty.Start(cmd)
if err != nil {
panic(err)
}
io.Copy(os.Stdout, f)
return nil
}

@ -0,0 +1,27 @@
/*
kratos 是Kratos的工具链提供新项目创建代码生成等功能
kartos build 本目录之下局部编译根目录全量编译
NAME:
kratos build
USAGE:
kratos build [arguments...]
EXAMPLE:
cd app && kratos build ./service/.. admin interface/.. tool/cache/...
kratos build
kartos init 新建新项目
USAGE:
kratos init [command options] [arguments...]
OPTIONS:
-n value 项目名
-o value 维护人
--grpc 是否是GRPC
EXAMPLE:
kratos init -n demo -o kratos
*/
package main

@ -0,0 +1,155 @@
package main
import (
"fmt"
"go/build"
"os"
"path/filepath"
"strings"
"github.com/urfave/cli"
"gopkg.in/AlecAivazis/survey.v1"
)
const (
_textModeFastInit = "一键初始化项目"
_textModeInteraction = "自定义项目参数"
_textYes = "是"
_textNo = "否"
)
func runInit(ctx *cli.Context) (err error) {
if ctx.NumFlags() == 0 {
if err = interact(); err != nil {
return
}
}
if !validate() {
return nil
}
if err = create(); err != nil {
fmt.Println("项目初始化失败: ", err.Error())
return nil
}
fmt.Printf("项目[%s]初始化成功!\n", p.Path)
return nil
}
func initPwd() (ok bool) {
pwd, err := os.Getwd()
if err != nil {
return
}
ps := strings.Split(pwd, string(os.PathSeparator))
plen := len(ps)
if plen < 1 {
// 至少要有一个目录层级:项目名
return
}
name := ps[plen-1]
if name == "" {
return
}
p.Name = name
p.Path = pwd
return true
}
func goPath() (gp string) {
gopaths := strings.Split(os.Getenv("GOPATH"), ":")
if len(gopaths) == 1 {
return gopaths[0]
}
pwd, err := os.Getwd()
if err != nil {
return
}
abspwd, err := filepath.Abs(pwd)
if err != nil {
return
}
for _, gopath := range gopaths {
absgp, err := filepath.Abs(gopath)
if err != nil {
return
}
if strings.HasPrefix(abspwd, absgp) {
return absgp
}
}
return build.Default.GOPATH
}
func interact() (err error) {
qs1 := &survey.Select{
Message: "你想怎么玩?",
Options: []string{_textModeFastInit, _textModeInteraction},
}
var ans1 string
if err = survey.AskOne(qs1, &ans1, nil); err != nil {
return
}
switch ans1 {
case _textModeFastInit:
if ok := initPwd(); !ok {
fmt.Println("快速初始化失败!")
}
return
case _textModeInteraction:
// go on
default:
return
}
qs := []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{
Message: "请输入项目名称:",
},
Validate: survey.Required,
},
{
Name: "owner",
Prompt: &survey.Input{
Message: "请输入项目负责人:",
},
},
{
Name: "useGRPC",
Prompt: &survey.Select{
Message: "是否使用 gRPC ?",
Options: []string{_textYes, _textNo},
Default: _textNo,
},
},
{
Name: "here",
Prompt: &survey.Select{
Message: "是否当前目录?默认为GOPATH下",
Options: []string{_textYes, _textNo},
Default: _textYes,
},
},
}
ans := struct {
Name string
Owner string
UseGRPC string
Here string
}{}
if err = survey.Ask(qs, &ans); err != nil {
return
}
p.Name = ans.Name
p.Owner = ans.Owner
if ans.UseGRPC == _textYes {
p.WithGRPC = true
}
if ans.UseGRPC == _textYes {
p.WithGRPC = true
}
if ans.Here == _textYes {
p.Here = true
}
return
}

@ -0,0 +1,72 @@
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "kratos"
app.Usage = "kratos tool"
app.Version = Version
app.Commands = []cli.Command{
{
Name: "build",
Aliases: []string{"b"},
Usage: "kratos build",
Action: buildAction,
},
{
Name: "init",
Aliases: []string{"i"},
Usage: "create new project",
Flags: []cli.Flag{
cli.StringFlag{
Name: "n",
Value: "",
Usage: "project name for create project",
Destination: &p.Name,
},
cli.StringFlag{
Name: "o",
Value: "",
Usage: "project owner for create project",
Destination: &p.Owner,
},
cli.BoolFlag{
Name: "grpc",
Usage: "whether to use grpc for create project",
Destination: &p.WithGRPC,
},
},
Action: runInit,
},
{
Name: "tool",
Aliases: []string{"t"},
Usage: "kratos tool",
Action: toolAction,
},
{
Name: "version",
Aliases: []string{"v"},
Usage: "kratos version",
Action: func(c *cli.Context) error {
fmt.Println(getVersion())
return nil
},
},
{
Name: "self-upgrade",
Usage: "kratos self-upgrade",
Action: upgradeAction,
},
}
err := app.Run(os.Args)
if err != nil {
panic(err)
}
}

@ -0,0 +1,174 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"strings"
"text/template"
)
// project project config
type project struct {
Name string
Owner string
Path string
WithGRPC bool
Here bool
}
const (
_tplTypeDao = iota
_tplTypeHTTPServer
_tplTypeAPIProto
_tplTypeService
_tplTypeMain
_tplTypeChangeLog
_tplTypeContributors
_tplTypeReadme
_tplTypeAppToml
_tplTypeMySQLToml
_tplTypeMCToml
_tplTypeRedisToml
_tplTypeHTTPToml
_tplTypeGRPCToml
_tplTypeModel
_tplTypeGRPCServer
_tplTypeAPIGenerate
)
var (
p project
// files type => path
files = map[int]string{
// init doc
_tplTypeChangeLog: "/CHANGELOG.md",
_tplTypeContributors: "/CONTRIBUTORS.md",
_tplTypeReadme: "/README.md",
// init project
_tplTypeMain: "/cmd/main.go",
_tplTypeDao: "/internal/dao/dao.go",
_tplTypeHTTPServer: "/internal/server/http/http.go",
_tplTypeService: "/internal/service/service.go",
_tplTypeModel: "/internal/model/model.go",
// init config
_tplTypeAppToml: "/configs/application.toml",
_tplTypeMySQLToml: "/configs/mysql.toml",
_tplTypeMCToml: "/configs/memcache.toml",
_tplTypeRedisToml: "/configs/redis.toml",
_tplTypeHTTPToml: "/configs/http.toml",
_tplTypeGRPCToml: "/configs/grpc.toml",
}
// tpls type => content
tpls = map[int]string{
_tplTypeDao: _tplDao,
_tplTypeHTTPServer: _tplHTTPServer,
_tplTypeAPIProto: _tplAPIProto,
_tplTypeAPIGenerate: _tplAPIGenerate,
_tplTypeMain: _tplMain,
_tplTypeChangeLog: _tplChangeLog,
_tplTypeContributors: _tplContributors,
_tplTypeReadme: _tplReadme,
_tplTypeMySQLToml: _tplMySQLToml,
_tplTypeMCToml: _tplMCToml,
_tplTypeRedisToml: _tplRedisToml,
_tplTypeAppToml: _tplAppToml,
_tplTypeHTTPToml: _tplHTTPToml,
_tplTypeModel: _tplModel,
}
)
func validate() (ok bool) {
if p.Name == "" {
fmt.Println("[-n] Invalid project name.")
return
}
if p.Path == "" {
if p.Here {
pwd, _ := os.Getwd()
p.Path = path.Join(pwd, p.Name)
} else {
p.Path = path.Join(goPath(), "src", p.Name)
}
}
return true
}
func create() (err error) {
if p.WithGRPC {
files[_tplTypeGRPCServer] = "/internal/server/grpc/server.go"
files[_tplTypeAPIProto] = "/api/api.proto"
files[_tplTypeAPIGenerate] = "/api/generate.go"
tpls[_tplTypeGRPCServer] = _tplGRPCServer
tpls[_tplTypeGRPCToml] = _tplGRPCToml
tpls[_tplTypeService] = _tplGPRCService
} else {
tpls[_tplTypeService] = _tplService
tpls[_tplTypeMain] = delgrpc(_tplMain)
}
if err = os.MkdirAll(p.Path, 0755); err != nil {
return
}
for t, v := range files {
i := strings.LastIndex(v, "/")
if i > 0 {
dir := v[:i]
if err = os.MkdirAll(p.Path+dir, 0755); err != nil {
return
}
}
if err = write(p.Path+v, tpls[t]); err != nil {
return
}
}
if p.WithGRPC {
if err = genpb(); err != nil {
return
}
}
return
}
func genpb() error {
cmd := exec.Command("go", "generate", p.Path+"/api/generate.go")
return cmd.Run()
}
func delgrpc(tpl string) string {
var buf bytes.Buffer
lines := strings.Split(tpl, "\n")
for _, l := range lines {
if strings.Contains(l, "grpc") {
continue
}
if strings.Contains(l, "warden") {
continue
}
buf.WriteString(l)
buf.WriteString("\n")
}
return buf.String()
}
func write(name, tpl string) (err error) {
data, err := parse(tpl)
if err != nil {
return
}
return ioutil.WriteFile(name, data, 0644)
}
func parse(s string) ([]byte, error) {
t, err := template.New("").Parse(s)
if err != nil {
return nil, err
}
var buf bytes.Buffer
if err = t.Execute(&buf, p); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

@ -0,0 +1,451 @@
package main
const (
_tplAppToml = `
# This is a TOML document. Boom~
`
_tplMySQLToml = `
[demo]
addr = "127.0.0.1:3306"
dsn = "{user}:{password}@tcp(127.0.0.1:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
readDSN = ["{user}:{password}@tcp(127.0.0.2:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8","{user}:{password}@tcp(127.0.0.3:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "200ms"
execTimeout = "300ms"
tranTimeout = "400ms"
`
_tplMCToml = `
demoExpire = "24h"
[demo]
name = "{{.Name}}"
proto = "tcp"
addr = "127.0.0.1:11211"
active = 50
idle = 10
dialTimeout = "100ms"
readTimeout = "200ms"
writeTimeout = "300ms"
idleTimeout = "80s"
`
_tplRedisToml = `
demoExpire = "24h"
[demo]
name = "{{.Name}}"
proto = "tcp"
addr = "127.0.0.1:6389"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
`
_tplHTTPToml = `
[server]
addr = "0.0.0.0:8000"
timeout = "1s"
`
_tplGRPCToml = `
[server]
addr = "0.0.0.0:9000"
timeout = "1s"
`
_tplChangeLog = `## {{.Name}}
### v1.0.0
1. 上线功能xxx
`
_tplMain = `package main
import (
"context"
"flag"
"os"
"os/signal"
"syscall"
"time"
"{{.Name}}/internal/server/grpc"
"{{.Name}}/internal/server/http"
"{{.Name}}/internal/service"
"github.com/bilibili/Kratos/pkg/conf/paladin"
"github.com/bilibili/Kratos/pkg/log"
)
func main() {
flag.Parse()
if err := paladin.Init(); err != nil {
panic(err)
}
log.Init(nil) // debug flag: log.dir={path}
defer log.Close()
log.Info("{{.Name}} start")
svc := service.New()
grpcSrv := grpc.New(svc)
httpSrv := http.New(svc)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
ctx, cancel := context.WithTimeout(context.Background(), 35*time.Second)
defer cancel()
grpcSrv.Shutdown(ctx)
httpSrv.Shutdown(ctx)
log.Info("{{.Name}} exit")
svc.Close()
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}
}
`
_tplContributors = `# Owner
{{.Owner}}
# Author
# Reviewer
`
_tplDao = `package dao
import (
"context"
"time"
"github.com/bilibili/Kratos/pkg/cache/memcache"
"github.com/bilibili/Kratos/pkg/cache/redis"
"github.com/bilibili/Kratos/pkg/conf/paladin"
"github.com/bilibili/Kratos/pkg/database/sql"
"github.com/bilibili/Kratos/pkg/log"
xtime "github.com/bilibili/Kratos/pkg/time"
)
// Dao dao.
type Dao struct {
db *sql.DB
redis *redis.Pool
redisExpire int32
mc *memcache.Memcache
mcExpire int32
}
func checkErr(err error) {
if err != nil {
panic(err)
}
}
// New new a dao and return.
func New() (dao *Dao) {
var (
dc struct {
Demo *sql.Config
}
rc struct {
Demo *redis.Config
DemoExpire xtime.Duration
}
mc struct {
Demo *memcache.Config
DemoExpire xtime.Duration
}
)
checkErr(paladin.Get("mysql.toml").UnmarshalTOML(&dc))
checkErr(paladin.Get("redis.toml").UnmarshalTOML(&rc))
checkErr(paladin.Get("memcache.toml").UnmarshalTOML(&mc))
dao = &Dao{
// mysql
db: sql.NewMySQL(dc.Demo),
// redis
redis: redis.NewPool(rc.Demo),
redisExpire: int32(time.Duration(rc.DemoExpire) / time.Second),
// memcache
mc: memcache.New(mc.Demo),
mcExpire: int32(time.Duration(mc.DemoExpire) / time.Second),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.mc.Close()
d.redis.Close()
d.db.Close()
}
// Ping ping the resource.
func (d *Dao) Ping(ctx context.Context) (err error) {
if err = d.pingMC(ctx); err != nil {
return
}
if err = d.pingRedis(ctx); err != nil {
return
}
return d.db.Ping(ctx)
}
func (d *Dao) pingMC(ctx context.Context) (err error) {
if err = d.mc.Set(ctx, &memcache.Item{Key: "ping", Value: []byte("pong"), Expiration: 0}); err != nil {
log.Error("conn.Set(PING) error(%v)", err)
}
return
}
func (d *Dao) pingRedis(ctx context.Context) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
if _, err = conn.Do("SET", "ping", "pong"); err != nil {
log.Error("conn.Set(PING) error(%v)", err)
}
return
}
`
_tplReadme = `# {{.Name}}
## 项目简介
1.
## 编译环境
## 依赖包
## 编译执行
`
_tplService = `package service
import (
"context"
"{{.Name}}/internal/dao"
"github.com/bilibili/Kratos/pkg/conf/paladin"
)
// Service service.
type Service struct {
ac *paladin.Map
dao *dao.Dao
}
// New new a service and return.
func New() (s *Service) {
var ac = new(paladin.TOML)
if err := paladin.Watch("application.toml", ac); err != nil {
panic(err)
}
s = &Service{
ac: ac,
dao: dao.New(),
}
return s
}
// Ping ping the resource.
func (s *Service) Ping(ctx context.Context) (err error) {
return s.dao.Ping(ctx)
}
// Close close the resource.
func (s *Service) Close() {
s.dao.Close()
}
`
_tplGPRCService = `package service
import (
"context"
"fmt"
pb "{{.Name}}/api"
"{{.Name}}/internal/dao"
"github.com/bilibili/Kratos/pkg/conf/paladin"
"github.com/golang/protobuf/ptypes/empty"
)
// Service service.
type Service struct {
ac *paladin.Map
dao *dao.Dao
}
// New new a service and return.
func New() (s *Service) {
var ac = new(paladin.TOML)
if err := paladin.Watch("application.toml", ac); err != nil {
panic(err)
}
s = &Service{
ac: ac,
dao: dao.New(),
}
return s
}
// SayHello grpc demo func.
func (s *Service) SayHello(ctx context.Context, req *pb.HelloReq) (reply *empty.Empty, err error) {
reply = new(empty.Empty)
fmt.Printf("hello %s", req.Name)
return
}
// Ping ping the resource.
func (s *Service) Ping(ctx context.Context) (err error) {
return s.dao.Ping(ctx)
}
// Close close the resource.
func (s *Service) Close() {
s.dao.Close()
}
`
_tplHTTPServer = `package http
import (
"net/http"
"{{.Name}}/internal/service"
"github.com/bilibili/Kratos/pkg/conf/paladin"
"github.com/bilibili/Kratos/pkg/log"
bm "github.com/bilibili/Kratos/pkg/net/http/blademaster"
"github.com/bilibili/Kratos/pkg/net/http/blademaster/middleware/verify"
)
var (
svc *service.Service
)
// New new a bm server.
func New(s *service.Service) (engine *bm.Engine) {
var (
hc struct {
Server *bm.ServerConfig
}
)
if err := paladin.Get("http.toml").UnmarshalTOML(&hc); err != nil {
if err != paladin.ErrNotExist {
panic(err)
}
}
svc = s
engine = bm.DefaultServer(hc.Server)
initRouter(engine, verify.New(nil))
if err := engine.Start(); err != nil {
panic(err)
}
return
}
func initRouter(e *bm.Engine, v *verify.Verify) {
e.Ping(ping)
e.Register(register)
g := e.Group("/{{.Name}}")
{
g.GET("/start", v.Verify, howToStart)
}
}
func ping(ctx *bm.Context) {
if err := svc.Ping(ctx); err != nil {
log.Error("ping error(%v)", err)
ctx.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}
// example for http request handler.
func howToStart(c *bm.Context) {
c.String(0, "Golang 大法好 !!!")
}
`
_tplAPIProto = `// 定义项目 API 的 proto 文件 可以同时描述 gRPC 和 HTTP API
// protobuf 文件参考:
// - https://developers.google.com/protocol-buffers/
// - TODO:待补充文档URL
// protobuf 生成 HTTP 工具:
// - TODO:待补充文档URL
// gRPC Golang Model:
// - TODO:待补充文档URL
// gRPC Golang Warden Gen:
// - TODO:待补充文档URL
// gRPC http 调试工具(无需pb文件):
// - TODO:待补充文档URL
// grpc 命令行调试工具(无需pb文件):
// - TODO:待补充文档URL
syntax = "proto3";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "google/protobuf/empty.proto";
// package 命名使用 {appid}.{version} 的方式, version 形如 v1, v2 ..
package demo.service.v1;
// NOTE: 最后请删除这些无用的注释 (゜-゜)つロ
option go_package = "api";
// do not generate getXXX() method
option (gogoproto.goproto_getters_all) = false;
service Demo {
rpc SayHello (HelloReq) returns (.google.protobuf.Empty);
}
message HelloReq {
string name = 1 [(gogoproto.moretags)='form:"name" validate:"required"'];
}
`
_tplAPIGenerate = `package api
// 生成 gRPC 代码
//go:generate TODO:待完善工具protoc.sh
`
_tplModel = `package model
`
_tplGRPCServer = `package grpc
import (
pb "{{.Name}}/api"
"{{.Name}}/internal/service"
"github.com/bilibili/Kratos/pkg/conf/paladin"
"github.com/bilibili/Kratos/pkg/net/rpc/warden"
)
// New new a grpc server.
func New(svc *service.Service) *warden.Server {
var rc struct {
Server *warden.ServerConfig
}
if err := paladin.Get("grpc.toml").UnmarshalTOML(&rc); err != nil {
if err != paladin.ErrNotExist {
panic(err)
}
}
ws := warden.NewServer(rc.Server)
pb.RegisterDemoServer(ws.Server(), svc)
ws, err := ws.Start()
if err != nil {
panic(err)
}
return ws
}
`
)

@ -0,0 +1,175 @@
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"sort"
"strings"
"time"
"github.com/fatih/color"
"github.com/urfave/cli"
)
func toolAction(c *cli.Context) (err error) {
if c.NArg() == 0 {
sort.Slice(toolIndexs, func(i, j int) bool { return toolIndexs[i].BuildTime.After(toolIndexs[j].BuildTime) })
for _, t := range toolIndexs {
updateTime := t.BuildTime.Format("2006/01/02")
fmt.Printf("%s%s: %s %s (%s) [%s]\n", color.HiMagentaString(t.Name), getNotice(t), color.HiCyanString(t.Summary), t.URL, t.Author, updateTime)
}
fmt.Println("\n执行 install 安装程序 如: kratos tool install demo")
fmt.Println("执行 工具名称 运行程序 如: kratos tool demo")
fmt.Println("\n安装全部工具: kratos tool install all")
return
}
if c.Args().First() == "install" {
name := c.Args().Get(1)
if name == "all" {
installAll()
} else {
install(name)
}
return
}
name := c.Args().First()
for _, t := range toolIndexs {
if name == t.Name {
if !t.installed() || t.updated() {
install(name)
}
pwd, _ := os.Getwd()
var args []string
if c.NArg() > 1 {
args = []string(c.Args())[1:]
}
runTool(t.Name, pwd, t.toolPath(), args)
return
}
}
fmt.Fprintf(os.Stderr, "还未安装 %s\n", name)
return
}
func upgradeAction(c *cli.Context) error {
install("kratos")
return nil
}
func install(name string) {
if name == "" {
fmt.Fprintf(os.Stderr, color.HiRedString("请填写要安装的工具名称\n"))
return
}
for _, t := range toolIndexs {
if name == t.Name {
t.install()
return
}
}
fmt.Fprintf(os.Stderr, color.HiRedString("安装失败 找不到 %s\n", name))
return
}
func installAll() {
for _, t := range toolIndexs {
if t.Install != "" {
t.install()
}
}
}
func getNotice(t *Tool) (notice string) {
if !t.supportOS() || t.Install == "" {
return
}
notice = color.HiGreenString("(未安装)")
if f, err := os.Stat(t.toolPath()); err == nil {
notice = color.HiBlueString("(已安装)")
if t.BuildTime.After(f.ModTime()) {
notice = color.RedString("(有更新)")
}
}
return
}
func runTool(name, dir, cmd string, args []string) (err error) {
toolCmd := &exec.Cmd{
Path: cmd,
Args: append([]string{cmd}, args...),
Dir: dir,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Env: os.Environ(),
}
if filepath.Base(cmd) == cmd {
var lp string
if lp, err = exec.LookPath(cmd); err == nil {
toolCmd.Path = lp
}
}
if err = toolCmd.Run(); err != nil {
if e, ok := err.(*exec.ExitError); !ok || !e.Exited() {
fmt.Fprintf(os.Stderr, "install %s: %v\n", name, err)
}
}
return
}
// Tool .
type Tool struct {
Name string `json:"name"`
BuildTime time.Time `json:"build_time"`
Install string `json:"install"`
Summary string `json:"summary"`
Platform []string `json:"platform"`
Author string `json:"author"`
URL string `json:"url"`
}
func (t Tool) supportOS() bool {
for _, p := range t.Platform {
if strings.ToLower(p) == runtime.GOOS {
return true
}
}
return false
}
func (t Tool) install() {
if t.Install == "" {
fmt.Fprintf(os.Stderr, color.RedString("%s: 自动安装失败 详情请查看文档 %s\n", t.Name, t.URL))
return
}
cmds := strings.Split(t.Install, " ")
if len(cmds) > 0 {
if err := runTool(t.Name, t.toolPath(), cmds[0], cmds[1:]); err == nil {
color.Green("%s: 安装成功!", t.Name)
}
}
}
func (t Tool) updated() bool {
if !t.supportOS() || t.Install == "" {
return false
}
if f, err := os.Stat(t.toolPath()); err == nil {
if t.BuildTime.After(f.ModTime()) {
return true
}
}
return false
}
func (t Tool) toolPath() string {
return filepath.Join(goPath(), "bin", t.Name)
}
func (t Tool) installed() bool {
_, err := os.Stat(t.toolPath())
return err == nil
}

@ -0,0 +1,15 @@
package main
import "time"
var toolIndexs = []*Tool{
&Tool{
Name: "kratos",
BuildTime: time.Date(2019, 4, 2, 0, 0, 0, 0, time.Local),
Install: "go get -u github.com/bilibili/Kratos/tool/kratos",
Summary: "Kratos工具集本体",
Platform: []string{"darwin", "linux", "windows"},
Author: "kratos",
URL: "wiki",
},
}

@ -0,0 +1,44 @@
package main
import (
"bytes"
"runtime"
"text/template"
)
var (
// Version is version
Version = "0.0.1"
// BuildTime is BuildTime
BuildTime = "2019/04/03"
)
// VersionOptions include version
type VersionOptions struct {
GitCommit string
Version string
BuildTime string
GoVersion string
Os string
Arch string
}
var versionTemplate = ` Version: {{.Version}}
Go version: {{.GoVersion}}
Built: {{.BuildTime}}
OS/Arch: {{.Os}}/{{.Arch}}
`
func getVersion() string {
var doc bytes.Buffer
vo := VersionOptions{
Version: Version,
BuildTime: BuildTime,
GoVersion: runtime.Version(),
Os: runtime.GOOS,
Arch: runtime.GOARCH,
}
tmpl, _ := template.New("version").Parse(versionTemplate)
tmpl.Execute(&doc, vo)
return doc.String()
}
Loading…
Cancel
Save