micro--go-micro
258 行
5.4 KiB
Go
258 行
5.4 KiB
Go
// Package new generates micro service templates
|
|
package new
|
|
|
|
import (
|
|
"fmt"
|
|
"go/build"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
"github.com/xlab/treeprint"
|
|
tmpl "go-micro.dev/v5/cmd/micro/cli/new/template"
|
|
)
|
|
|
|
func protoComments(goDir, alias string) []string {
|
|
return []string{
|
|
"\ndownload protoc zip packages (protoc-$VERSION-$PLATFORM.zip) and install:\n",
|
|
"visit https://github.com/protocolbuffers/protobuf/releases",
|
|
"\ncompile the proto file " + alias + ".proto:\n",
|
|
"cd " + alias,
|
|
"go mod tidy",
|
|
"make proto\n",
|
|
}
|
|
}
|
|
|
|
type config struct {
|
|
// foo
|
|
Alias string
|
|
// github.com/micro/foo
|
|
Dir string
|
|
// $GOPATH/src/github.com/micro/foo
|
|
GoDir string
|
|
// $GOPATH
|
|
GoPath string
|
|
// UseGoPath
|
|
UseGoPath bool
|
|
// Files
|
|
Files []file
|
|
// Comments
|
|
Comments []string
|
|
}
|
|
|
|
type file struct {
|
|
Path string
|
|
Tmpl string
|
|
}
|
|
|
|
func write(c config, file, tmpl string) error {
|
|
fn := template.FuncMap{
|
|
"title": func(s string) string {
|
|
return strings.ReplaceAll(strings.Title(s), "-", "")
|
|
},
|
|
"dehyphen": func(s string) string {
|
|
return strings.ReplaceAll(s, "-", "")
|
|
},
|
|
"lower": func(s string) string {
|
|
return strings.ToLower(s)
|
|
},
|
|
}
|
|
|
|
f, err := os.Create(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
t, err := template.New("f").Funcs(fn).Parse(tmpl)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return t.Execute(f, c)
|
|
}
|
|
|
|
func create(c config) error {
|
|
// check if dir exists
|
|
if _, err := os.Stat(c.Dir); !os.IsNotExist(err) {
|
|
return fmt.Errorf("%s already exists", c.Dir)
|
|
}
|
|
|
|
fmt.Printf("Creating service %s\n\n", c.Alias)
|
|
|
|
t := treeprint.New()
|
|
|
|
// write the files
|
|
for _, file := range c.Files {
|
|
f := filepath.Join(c.Dir, file.Path)
|
|
dir := filepath.Dir(f)
|
|
|
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
addFileToTree(t, file.Path)
|
|
if err := write(c, f, file.Tmpl); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// print tree
|
|
fmt.Println(t.String())
|
|
|
|
for _, comment := range c.Comments {
|
|
fmt.Println(comment)
|
|
}
|
|
|
|
// just wait
|
|
<-time.After(time.Millisecond * 250)
|
|
|
|
return nil
|
|
}
|
|
|
|
func addFileToTree(root treeprint.Tree, file string) {
|
|
split := strings.Split(file, "/")
|
|
curr := root
|
|
for i := 0; i < len(split)-1; i++ {
|
|
n := curr.FindByValue(split[i])
|
|
if n != nil {
|
|
curr = n
|
|
} else {
|
|
curr = curr.AddBranch(split[i])
|
|
}
|
|
}
|
|
if curr.FindByValue(split[len(split)-1]) == nil {
|
|
curr.AddNode(split[len(split)-1])
|
|
}
|
|
}
|
|
|
|
func Run(ctx *cli.Context) error {
|
|
dir := ctx.Args().First()
|
|
if len(dir) == 0 {
|
|
fmt.Println("specify service name")
|
|
return nil
|
|
}
|
|
|
|
// check if the path is absolute, we don't want this
|
|
// we want to a relative path so we can install in GOPATH
|
|
if path.IsAbs(dir) {
|
|
fmt.Println("require relative path as service will be installed in GOPATH")
|
|
return nil
|
|
}
|
|
|
|
// Check for protoc
|
|
if _, err := exec.LookPath("protoc"); err != nil {
|
|
fmt.Println("WARNING: protoc is not installed or not in your PATH.")
|
|
fmt.Println("Please install protoc from https://github.com/protocolbuffers/protobuf/releases")
|
|
fmt.Println("After installing, re-run 'make proto' in your service directory if needed.")
|
|
}
|
|
|
|
var goPath string
|
|
var goDir string
|
|
|
|
goPath = build.Default.GOPATH
|
|
|
|
// don't know GOPATH, runaway....
|
|
if len(goPath) == 0 {
|
|
fmt.Println("unknown GOPATH")
|
|
return nil
|
|
}
|
|
|
|
// attempt to split path if not windows
|
|
if runtime.GOOS == "windows" {
|
|
goPath = strings.Split(goPath, ";")[0]
|
|
} else {
|
|
goPath = strings.Split(goPath, ":")[0]
|
|
}
|
|
goDir = filepath.Join(goPath, "src", path.Clean(dir))
|
|
|
|
c := config{
|
|
Alias: dir,
|
|
Comments: nil, // Remove redundant protoComments
|
|
Dir: dir,
|
|
GoDir: goDir,
|
|
GoPath: goPath,
|
|
UseGoPath: false,
|
|
Files: []file{
|
|
{"main.go", tmpl.MainSRV},
|
|
{"handler/" + dir + ".go", tmpl.HandlerSRV},
|
|
{"proto/" + dir + ".proto", tmpl.ProtoSRV},
|
|
{"Makefile", tmpl.Makefile},
|
|
{"README.md", tmpl.Readme},
|
|
{".gitignore", tmpl.GitIgnore},
|
|
},
|
|
}
|
|
|
|
// set gomodule
|
|
if os.Getenv("GO111MODULE") != "off" {
|
|
c.Files = append(c.Files, file{"go.mod", tmpl.Module})
|
|
}
|
|
|
|
// create the files
|
|
if err := create(c); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Run go mod tidy and make proto
|
|
fmt.Println("\nRunning 'go mod tidy' and 'make proto'...")
|
|
if err := runInDir(dir, "go mod tidy"); err != nil {
|
|
fmt.Printf("Error running 'go mod tidy': %v\n", err)
|
|
}
|
|
if err := runInDir(dir, "make proto"); err != nil {
|
|
fmt.Printf("Error running 'make proto': %v\n", err)
|
|
}
|
|
|
|
// Print updated tree including generated files
|
|
fmt.Println("\nProject structure after 'make proto':")
|
|
printTree(dir)
|
|
|
|
fmt.Println("\nService created successfully! Start coding in your new service directory.")
|
|
return nil
|
|
}
|
|
|
|
func runInDir(dir, cmd string) error {
|
|
parts := strings.Fields(cmd)
|
|
c := exec.Command(parts[0], parts[1:]...)
|
|
c.Dir = dir
|
|
c.Stdout = os.Stdout
|
|
c.Stderr = os.Stderr
|
|
return c.Run()
|
|
}
|
|
|
|
func printTree(dir string) {
|
|
t := treeprint.New()
|
|
walk := func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rel, _ := filepath.Rel(dir, path)
|
|
if rel == "." {
|
|
return nil
|
|
}
|
|
parts := strings.Split(rel, string(os.PathSeparator))
|
|
curr := t
|
|
for i := 0; i < len(parts)-1; i++ {
|
|
n := curr.FindByValue(parts[i])
|
|
if n != nil {
|
|
curr = n
|
|
} else {
|
|
curr = curr.AddBranch(parts[i])
|
|
}
|
|
}
|
|
if !info.IsDir() {
|
|
curr.AddNode(parts[len(parts)-1])
|
|
}
|
|
return nil
|
|
}
|
|
filepath.Walk(dir, walk)
|
|
fmt.Println(t.String())
|
|
}
|