feat: create project command

This commit is contained in:
Young Xu 2023-03-19 23:03:52 +08:00
parent 0ea42a3d33
commit 15ecf1e90a
Signed by: xuthus5
GPG Key ID: A23CF9620CBB55F9
14 changed files with 406 additions and 3 deletions

View File

@ -9,7 +9,7 @@ import (
var (
rootCmd = &cobra.Command{
Use: "coco",
Short: "golang project generator",
Short: "golang project toolkit",
}
)
@ -24,6 +24,7 @@ func init() {
QuoteEmptyFields: false,
})
rootCmd.AddCommand(bootstrap.Update())
rootCmd.AddCommand(bootstrap.CreateProject())
}
func main() {

View File

@ -14,7 +14,7 @@ func Update() *cobra.Command {
var configPath string
return &cobra.Command{
Use: "update",
Short: "coco tool update",
Short: "coco update",
PreRun: func(cmd *cobra.Command, args []string) {
configPath = runtime.ReplaceEachSlash(fmt.Sprintf("%s/.coco.yaml", runtime.GetHomeDir()))
_ = yaml.Read(configPath, &config)
@ -38,7 +38,7 @@ func Update() *cobra.Command {
// exec update
repo := fmt.Sprintf("gitter.top/coco/bootstrap/coco/...@%s", commitID)
if _, err := runtime.Exec("go", "install", repo); err != nil {
logrus.Errorf("update coco failed: %v", err)
logrus.Errorf("update bootstrap failed: %v", err)
return
}

36
create_project.go Normal file
View File

@ -0,0 +1,36 @@
package bootstrap
import (
"fmt"
"github.com/spf13/cobra"
"gitter.top/coco/bootstrap/template"
"os"
"path"
)
func CreateProject() *cobra.Command {
var name, output string
cmd := &cobra.Command{
Use: "new",
Short: "create new project",
PreRun: func(cmd *cobra.Command, args []string) {
},
Run: func(cmd *cobra.Command, args []string) {
b := &template.Builder{
Path: output,
Name: name,
}
b.Path = path.Join(output, b.Name)
if err := b.Build(); err != nil {
_, _ = fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
},
}
cmd.Flags().StringVar(&name, "name", "CocoDemo", "project name")
cmd.Flags().StringVar(&output, "path", ".", "project path")
return cmd
}

82
template/creator.go Normal file
View File

@ -0,0 +1,82 @@
package template
import (
"bytes"
"fmt"
"os"
"strings"
"text/template"
)
var (
scaffold = map[string]string{
"/go.mod": templateModule,
"/.coco": TemplateRc,
"/.gitignore": templateGitignore,
"/module_update.sh": templateModuleUpdate,
"/cmd/api.go": templateCmdNewApiServer,
"/cmd/exec.go": templateCmdExec,
"/config_dev.yaml": templateDevYaml,
"/config_prod.yaml": templateProdYaml,
"/config/config.go": templateConfig,
"/model/model.proto": templateProto,
"/internal/logic/demo.go": templateLogic,
"/internal/router/router.go": templateRouter,
"/internal/services/demo_service.go": templateServices,
"/internal/controller/demo_module.proto": templateDemoModuleProtoDefine,
}
)
type Builder struct {
Name string
Path string
SSH string
}
func (b *Builder) Build() error {
if err := os.MkdirAll(b.Path, 0755); err != nil {
return err
}
if err := b.write(b.Path+"/"+b.Name+".go", templateMain); err != nil {
return err
}
for sr, v := range scaffold {
i := strings.LastIndex(sr, "/")
if i > 0 {
dir := sr[:i]
if err := os.MkdirAll(b.Path+dir, 0755); err != nil {
return err
}
}
if err := b.write(b.Path+sr, v); err != nil {
return err
}
}
return nil
}
func (b *Builder) write(name, tpl string) (err error) {
defer func() {
if err := recover(); err != nil {
fmt.Println("Failed")
}
}()
fmt.Printf("create %s \n", name)
data, err := b.parse(tpl)
if err != nil {
return
}
return os.WriteFile(name, data, 0644)
}
func (b *Builder) 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, b); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@ -0,0 +1,3 @@
package template
const TemplateRc = `freq_to: .`

View File

@ -0,0 +1,83 @@
package template
const templateCmdExec = `package cmd
import (
"fmt"
nested "github.com/antonfisher/nested-logrus-formatter"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"os"
"{{.Name}}/config"
"runtime"
"strings"
)
var (
rootCmd = &cobra.Command{}
cfgFile string
)
func Execute() {
// 预加载配置文件
loadConfig()
if err := rootCmd.Execute(); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
var getServeDir = func(path string) string {
var run, _ = os.Getwd()
return strings.Replace(path, run, ".", -1)
}
var formatter = &nested.Formatter{
NoColors: false,
HideKeys: true,
TimestampFormat: "2006-01-02 15:04:05",
CallerFirst: true,
CustomCallerFormatter: func(f *runtime.Frame) string {
s := strings.Split(f.Function, ".")
funcName := s[len(s)-1]
return fmt.Sprintf(" [%s:%d][%s()]", getServeDir(f.File), f.Line, funcName)
},
}
logrus.SetFormatter(formatter)
logrus.SetReportCaller(true)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "config_local.yaml", "config file")
rootCmd.AddCommand(apiServerCommand) // API服务
}
func loadConfig() {
// 初始化配置文件
config.New(cfgFile)
conf := config.Get()
if err := conf.Load(); err != nil {
panic(err)
}
}
`
const templateCmdNewApiServer = `package cmd
import (
"fmt"
"github.com/spf13/cobra"
"os"
"{{.Name}}/internal/router"
)
var (
apiServerCommand = &cobra.Command{
Use: "api",
Short: "start api server",
Long: "start api server",
Run: func(cmd *cobra.Command, args []string) {
router := router.NewRouter()
router.Register()
},
}
)
`

View File

@ -0,0 +1,52 @@
package template
const templateConfig = `package config
import (
"fmt"
"gopkg.in/yaml.v3"
"io/ioutil"
"os"
)
type ServerConfig struct {
ServerName string ` + "`" + `yaml:"server-name"` + "`" + `
ServerListen string ` + "`" + `yaml:"server-listen"` + "`" + `
Environment string ` + "`" + `yaml:"environment"` + "`" + `
}
type ServerConf struct {
ConfigFile string
ServerConfig ` + "`" + `yaml:",inline"` + "`" + `
}
var conf *ServerConf
func New(fileName string) {
conf = new(ServerConf)
conf.ConfigFile = fileName
}
func Get() *ServerConf {
if conf == nil {
panic(fmt.Errorf("config not init"))
}
return conf
}
func (receiver *ServerConf) Load() error {
f, err := os.Open(receiver.ConfigFile)
if err != nil {
return err
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return err
}
if err := yaml.Unmarshal(data, receiver); err != nil {
return err
}
return nil
}
`

54
template/template_demo.go Normal file
View File

@ -0,0 +1,54 @@
package template
const templateDemoModuleProtoDefine = `syntax = "proto3";
package controller;
option go_package = "{{.Name}}/internal/controller";
// @route_group: true
// @route_api: /api/test
// @gen_to: ./internal/controller/demo_controller.go
service DemoAPI {
// @desc: echo
// @author: 匿名
// @method: POST
// @api: /ping
rpc Ping (PingReq) returns (PingResp);
}
message PingReq {
string ping = 1; // 输入字符串 我会将该字符串回显给你
}
message PingResp {
string pong = 1; // 将输入的字符串原样输出
}
`
const templateServices = `package services
import "context"
type Test struct {
Appid string
}
func (test *Test) Test(c context.Context) error {
return nil
}
`
const templateLogic = `package logic
import "context"
type Test struct {
Appid string
}
func (test *Test) Test(c context.Context) error {
return nil
}
`

View File

@ -0,0 +1,9 @@
package template
const templateGitignore = `.idea
go.sum
{{.Name}}
{{.Name}}.exe
{{.Name}}.exe~
config_local.yaml
`

10
template/template_main.go Normal file
View File

@ -0,0 +1,10 @@
package template
const templateMain = `package main
import "{{.Name}}/cmd"
func main() {
cmd.Execute()
}
`

View File

@ -0,0 +1,8 @@
package template
const templateProto = `syntax = "proto3";
package model;
option go_package = "{{.Name}}/model";
`

View File

@ -0,0 +1,22 @@
package template
const templateModule = `module {{.Name}}
go 1.18
`
const templateModuleUpdate = `#!/bin/bash
# 该脚本用来自动全量更新该项目中用到的module
go mod tidy
match_required=$(cat go.mod | grep -zoE "\((.*?)\)" | grep -zoP "(?<=\()[^\)]*(?=\))" | awk -F ' ' '{if(NF==2){print $1}}')
for i in $match_required;do
echo $i "updating..."
go get -u "$i";
done
go mod tidy
`

View File

@ -0,0 +1,32 @@
package template
const templateRouter = `package router
import (
"fmt"
"gitter.top/coco/coco/core"
"github.com/gin-gonic/gin"
"os"
"{{.Name}}/config"
"{{.Name}}/infra/middleware"
)
type Router struct{}
func NewRouter() *Router {
return &Router{}
}
// Register 注册路由
func (receiver *Router) Register() error {
cfg := config.Get()
// 从这里开始实例化路由注册器
var register = core.NewRegister()
register.DefaultRouter(core.WithListenAddress(cfg.ServerListen),
core.WithGinMode(gin.ReleaseMode), core.WithCors(), core.WithRecovery())
register.PreRun(nil)
//reg.RegisterStruct(AutoGenXXXRouterMap, &XXX{})
_, _ = fmt.Fprintf(os.Stdout, "api server: %s\n", cfg.ServerListen)
register.Run()
}
`

11
template/template_yaml.go Normal file
View File

@ -0,0 +1,11 @@
package template
const templateDevYaml = `server-name: {{.Name}}
server-listen: 0.0.0.0:12580
environment: dev
`
const templateProdYaml = `server-name: {{.Name}}
server-listen: 0.0.0.0:12580
environment: prod
`