add kratos tool (#4)
parent
d0b98db00c
commit
7fc7de272c
@ -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…
Reference in new issue