add the copy-proto command

This commit is contained in:
zhuyasen 2023-08-29 22:15:02 +08:00
parent f6149789f1
commit 29808ab715
16 changed files with 333 additions and 87 deletions

View File

@ -170,11 +170,17 @@ deploy-binary: binary-package
.PHONY: patch
# patch some dependent code, such as types.proto, mysql initialization code. e.g. make patch TYPE=types-pb , make patch TYPE=mysql-redis-init
# patch some dependent code, such as types.proto, mysql initialization code. e.g. make patch TYPE=types-pb , make patch TYPE=mysql-init
patch:
@bash scripts/patch.sh $(TYPE)
.PHONY: copy-proto
# copy proto file from the rpc server directory, multiple directories separated by commas. e.g. make copy-proto SERVER=yourServerDir
copy-proto:
@bash scripts/copy-proto.sh $(SERVER)
.PHONY: clean
# clean binary file, cover.out, template file
clean:

View File

@ -1,23 +0,0 @@
package commands
import (
"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
"github.com/spf13/cobra"
)
// GenCommand generate dependency code
func GenCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "gen",
Short: "Generate dependency code, e.g. mysql and redis initialization code, types.proto",
Long: `generate dependency code.`,
SilenceErrors: true,
SilenceUsage: true,
}
cmd.AddCommand(
generate.MysqlAndRedisCommand(),
generate.TypesPbCommand(),
)
return cmd
}

View File

@ -34,12 +34,12 @@ type mergeParam struct {
splitLineMark []byte // file Contents Partition Line Marker
mark string // code mark strings or regular expressions
isLineCode bool // true:handles line code, false:handles code blocks
parseCode func(date []byte, markStr string) []code // parsing code method
parseCode func(data []byte, markStr string) []code // parsing code method
dt string // character form of date and time
backupDir string // backup Code Catalog
}
func newMergeParam(dir string, mark string, isLineCode bool, parseCode func(date []byte, markStr string) []code) *mergeParam {
func newMergeParam(dir string, mark string, isLineCode bool, parseCode func(data []byte, markStr string) []code) *mergeParam {
return &mergeParam{
dir: dir,
fuzzyFilename: defaultFuzzyFilename,
@ -77,7 +77,7 @@ func (m *mergeParam) runMerge() {
continue
}
if successFile != "" {
fmt.Printf("merge code to '%s' successfully.\n", successFile)
fmt.Printf("merge code to \"%s\" successfully.\n", successFile)
}
}
}
@ -233,9 +233,9 @@ func mergeCode(oldCode []byte, addCode []byte, position []byte) []byte {
// ------------- parsing the core of data in the internal/ecode directory -------------
func parseFromECode(date []byte, markStr string) []code {
func parseFromECode(data []byte, markStr string) []code {
var codes []code
buf := bufio.NewReader(bytes.NewReader(date))
buf := bufio.NewReader(bytes.NewReader(data))
for {
line, err := buf.ReadString('\n')
if err != nil {
@ -264,9 +264,9 @@ func getECodeMarkName(str string, markStr string) string {
// ------------- parsing the core of data in the internal/routers directory -------------
func parseFromRouters(date []byte, markStr string) []code {
func parseFromRouters(data []byte, markStr string) []code {
var codes []code
buf := bufio.NewReader(bytes.NewReader(date))
buf := bufio.NewReader(bytes.NewReader(data))
for {
line, err := buf.ReadString('\n')
if err != nil {

View File

@ -6,8 +6,8 @@ import (
"github.com/spf13/cobra"
)
// MicroCommand micro commands
func MicroCommand() *cobra.Command {
// GenMicroCommand generate micro service code
func GenMicroCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "micro",
Short: "Generate proto, model, cache, dao, service, rpc, rpc-gw, rpc-cli codes",

View File

@ -0,0 +1,27 @@
package commands
import (
"github.com/zhufuyi/sponge/cmd/sponge/commands/patch"
"github.com/spf13/cobra"
)
// PatchCommand patch server code
func PatchCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "patch",
Short: "Command set for patching server code",
Long: `command set for patching server code.`,
SilenceErrors: true,
SilenceUsage: true,
}
cmd.AddCommand(
patch.DeleteJSONOmitemptyCommand(),
patch.GenMysqlInitCommand(),
patch.GenTypesPbCommand(),
patch.CopyProtoCommand(),
)
return cmd
}

View File

@ -0,0 +1,203 @@
package patch
import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/zhufuyi/sponge/pkg/gobash"
"github.com/zhufuyi/sponge/pkg/gofile"
"github.com/spf13/cobra"
)
var copyCount = 0
// CopyProtoCommand copy proto file from the rpc server directory
func CopyProtoCommand() *cobra.Command {
var (
serverDir string // server dir
versionFolder string // proto file version folder
outPath string // output directory
)
cmd := &cobra.Command{
Use: "copy-proto",
Short: "Copy proto file from the rpc server directory",
Long: `copy proto file from the rpc server, if the proto file exists, it will be forced to overwrite it,
don't worry about losing the proto file after overwriting it, before copying proto it will be backed up to
the directory /tmp/sponge_copy_backup_proto_files.
Examples:
# copy proto file from a rpc server directory
sponge patch copy-proto --server-dir=./rpc-server
# copy proto file from multiple rpc servers directory
sponge patch copy-proto --server-dir=./rpc-server1,./rpc-server2
`,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
if !gofile.IsExists(outPath) {
_ = os.MkdirAll(outPath, 0766)
}
serverDirs := strings.Split(serverDir, ",")
for _, dir := range serverDirs {
sn, err := getServerName(dir)
if err != nil {
return err
}
err = copyProtoFiles(dir, sn, versionFolder, outPath)
if err != nil {
return err
}
}
if copyCount == 0 {
fmt.Printf("\nno proto files to copy, server-dir = %v\n", serverDirs)
return nil
} else {
fmt.Printf("\ncopy proto files successfully, out = %s\n", outPath)
}
return nil
},
}
cmd.Flags().StringVarP(&serverDir, "server-dir", "s", "", "server directory, multiple names separated by commas")
_ = cmd.MarkFlagRequired("server-dir")
cmd.Flags().StringVarP(&versionFolder, "version-folder", "v", "v1", "proto file version folder")
cmd.Flags().StringVarP(&outPath, "out", "o", "./api", "output directory, if the proto file already exists, it will be overwritten directly")
return cmd
}
func getServerName(dir string) (string, error) {
if dir == "" {
return "", errors.New("param 'server-dir' is empty")
}
data, err := os.ReadFile(dir + "/docs/gen.info")
if err != nil {
return "", err
}
ms := strings.Split(string(data), ",")
if len(ms) != 2 {
return "", errors.New("not found server name in docs/gen.info")
}
return ms[1], nil
}
func copyProtoFiles(dir string, serverName string, versionFolder string, outPath string) error {
srcProtoFolder := dir + "/api/" + serverName + "/" + versionFolder
targetProtoFolder := outPath + "/" + serverName + "/" + versionFolder
protoFiles, err := gofile.ListFiles(srcProtoFolder, gofile.WithSuffix(".proto"))
if err != nil {
return err
}
if len(protoFiles) > 0 {
err = backupProtoFiles(outPath)
if err != nil {
return err
}
fmt.Println()
}
for _, pf := range protoFiles {
data, err := os.ReadFile(pf)
if err != nil {
return err
}
err = copyDependencyProtoFile(data, dir, outPath)
if err != nil {
return err
}
targetProtoFile := targetProtoFolder + "/" + gofile.GetFilename(pf)
err = copyProtoFile(pf, targetProtoFile)
if err != nil {
return err
}
}
return nil
}
func copyDependencyProtoFile(data []byte, dir string, outPath string) error {
regStr := `import(.*?)"api/([\w\W]*?.proto)`
reg := regexp.MustCompile(regStr)
match := reg.FindAllStringSubmatch(string(data), -1)
if len(match) == 0 {
return nil
}
var pfs []string
for _, v := range match {
if len(v) == 3 {
pfs = append(pfs, v[2])
}
}
for _, pf := range pfs {
srcProtoFile := dir + "/api/" + pf
targetProtoFile := outPath + "/" + pf
pData, err := os.ReadFile(srcProtoFile)
if err != nil {
return err
}
err = copyProtoFile(srcProtoFile, targetProtoFile)
if err != nil {
return err
}
if copyCount > 1000 {
return errors.New("import dependencies circle or too many files")
}
err = copyDependencyProtoFile(pData, dir, outPath)
if err != nil {
return err
}
}
return nil
}
func copyProtoFile(srcProtoFile string, targetProtoFile string) error {
targetProtoDir := gofile.GetFileDir(targetProtoFile)
_ = os.MkdirAll(targetProtoDir, 0766)
fmt.Printf("copy \"%s\" --> \"%s\"\n", srcProtoFile, targetProtoFile)
_, err := gobash.Exec("cp", "-f", srcProtoFile, targetProtoFile)
if err != nil {
return err
}
copyCount++
return nil
}
func backupProtoFiles(outPath string) error {
prefixPath, err := filepath.Abs(outPath)
if err != nil {
return err
}
pfs, _ := gofile.ListFiles(outPath, gofile.WithSuffix(".proto"))
backupDir := os.TempDir() + gofile.GetPathDelimiter() + "sponge_copy_backup_proto_files" +
gofile.GetPathDelimiter() + time.Now().Format("20060102T150405")
for _, srcProtoFile := range pfs {
suffixPath := strings.ReplaceAll(srcProtoFile, prefixPath, "")
targetProtoFile := backupDir + suffixPath
targetProtoDir := gofile.GetFileDir(targetProtoFile)
_ = os.MkdirAll(targetProtoDir, 0744)
_, err = gobash.Exec("cp", srcProtoFile, targetProtoFile)
if err != nil {
return err
}
}
return nil
}

View File

@ -1,4 +1,4 @@
package generate
package patch
import (
"bytes"
@ -24,10 +24,10 @@ func DeleteJSONOmitemptyCommand() *cobra.Command {
Examples:
# delete all files that include the omitempty character
sponge del-omitempty --dir=./api
sponge patch del-omitempty --dir=./api
# delete the specified suffix file including the omitempty character
sponge del-omitempty --dir=./api --suffix-name=pb.go
sponge patch del-omitempty --dir=./api --suffix-name=pb.go
`,
SilenceErrors: true,

View File

@ -1,18 +1,20 @@
package generate
package patch
import (
"errors"
"fmt"
"os"
"strings"
"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
"github.com/zhufuyi/sponge/pkg/gofile"
"github.com/zhufuyi/sponge/pkg/replacer"
"github.com/spf13/cobra"
)
// MysqlAndRedisCommand generate mysql and redis initialization code
func MysqlAndRedisCommand() *cobra.Command {
// GenMysqlInitCommand generate mysql initialization code
func GenMysqlInitCommand() *cobra.Command {
var (
moduleName string // go.mod module name
outPath string // output directory
@ -20,16 +22,16 @@ func MysqlAndRedisCommand() *cobra.Command {
)
cmd := &cobra.Command{
Use: "mysql-redis-init",
Short: "Generate mysql and redis initialization code",
Long: `generate mysql and redis initialization code
Use: "gen-mysql-init",
Short: "Generate mysql initialization code",
Long: `generate mysql initialization code
Examples:
# generate mysql and redis initialization code.
sponge gen mysql-redis-init --module-name=yourModuleName
# generate mysql initialization code.
sponge patch gen-mysql-init --module-name=yourModuleName
# generate mysql and redis initialization code, and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge gen mysql-redis-init --out=./yourServerDir
# generate mysql initialization code, and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge patch gen-mysql-init --out=./yourServerDir
`,
SilenceErrors: true,
SilenceUsage: true,
@ -38,7 +40,7 @@ Examples:
if mdName != "" {
moduleName = mdName
} else if moduleName == "" {
return fmt.Errorf(`required flag(s) "module-name" not set, use "sponge gen mysql-redis-init -h" for help`)
return fmt.Errorf(`required flag(s) "module-name" not set, use "sponge patch gen-mysql-init -h" for help`)
}
var isEmpty bool
@ -83,8 +85,8 @@ using help:
}
func runMysqlCliCommand(moduleName string, outPath string) (string, error) {
subTplName := "mysql-redis-init"
r := Replacers[TplNameSponge]
subTplName := "mysql-init"
r := generate.Replacers[generate.TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
}
@ -93,7 +95,7 @@ func runMysqlCliCommand(moduleName string, outPath string) (string, error) {
subDirs := []string{"internal/model"} // only the specified subdirectory is processed, if empty or no subdirectory is specified, it means all files
ignoreDirs := []string{} // specify the directory in the subdirectory where processing is ignored
ignoreFiles := []string{ // specify the files in the subdirectory to be ignored for processing
"userExample.go",
"userExample.go", "init_test.go",
}
r.SetSubDirsAndFiles(subDirs)
@ -127,3 +129,21 @@ func addMysqlAndRedisInitFields(moduleName string) []replacer.Field {
return fields
}
// get moduleName and serverName from directory
func getNamesFromOutDir(dir string) (moduleName string, serverName string) {
if dir == "" {
return "", ""
}
data, err := os.ReadFile(dir + "/docs/gen.info")
if err != nil {
return "", ""
}
ms := strings.Split(string(data), ",")
if len(ms) != 2 {
return "", ""
}
return ms[0], ms[1]
}

View File

@ -1,18 +1,19 @@
package generate
package patch
import (
"errors"
"fmt"
"strings"
"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
"github.com/zhufuyi/sponge/pkg/gofile"
"github.com/zhufuyi/sponge/pkg/replacer"
"github.com/spf13/cobra"
)
// TypesPbCommand generate types.proto file
func TypesPbCommand() *cobra.Command {
// GenTypesPbCommand generate types.proto code
func GenTypesPbCommand() *cobra.Command {
var (
moduleName string // go.mod module name
outPath string // output directory
@ -20,16 +21,16 @@ func TypesPbCommand() *cobra.Command {
)
cmd := &cobra.Command{
Use: "types-pb",
Short: "Generate types.proto file",
Long: `generate types.proto file
Use: "gen-types-pb",
Short: "Generate types.proto code",
Long: `generate types.proto code
Examples:
# generate types.proto file.
sponge gen types-pb --module-name=yourModuleName
# generate types.proto code.
sponge patch gen-types-pb --module-name=yourModuleName
# generate types.proto file and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge gen types-pb --out=./yourServerDir
# generate types.proto code and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge patch gen-types-pb --out=./yourServerDir
`,
SilenceErrors: true,
SilenceUsage: true,
@ -38,7 +39,7 @@ Examples:
if mdName != "" {
moduleName = mdName
} else if moduleName == "" {
return fmt.Errorf(`required flag(s) "module-name" not set, use "sponge gen types-pb -h" for help`)
return fmt.Errorf(`required flag(s) "module-name" not set, use "sponge patch gen-types-pb -h" for help`)
}
var isEmpty bool
@ -84,7 +85,7 @@ using help:
func runTypesPbCommand(moduleName string, outPath string) (string, error) {
subTplName := "types-pb"
r := Replacers[TplNameSponge]
r := generate.Replacers[generate.TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
}

View File

@ -19,8 +19,8 @@ var (
func NewRootCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "sponge",
Long: `sponge is a powerful tool for generating web and microservice code, a microservice framework
based on gin and grpc encapsulation, and an open source framework for rapid application development.`,
Long: `sponge is a powerful golang productivity tool that integrates automatic code generation,
web and microservice framework, general basic development framework.`,
SilenceErrors: true,
SilenceUsage: true,
Version: getVersion(),
@ -30,13 +30,12 @@ based on gin and grpc encapsulation, and an open source framework for rapid appl
InitCommand(),
UpgradeCommand(),
ToolsCommand(),
NewWebCommand(),
MicroCommand(),
GenWebCommand(),
GenMicroCommand(),
generate.ConfigCommand(),
NewRunCommand(),
generate.DeleteJSONOmitemptyCommand(),
OpenUICommand(),
MergeCommand(),
GenCommand(),
PatchCommand(),
)
return cmd

View File

@ -12,13 +12,13 @@ import (
var servicePort = 24631
// NewRunCommand sponge run commands
func NewRunCommand() *cobra.Command {
// OpenUICommand open the sponge UI interface
func OpenUICommand() *cobra.Command {
var isLog bool
cmd := &cobra.Command{
Use: "run",
Short: "Open the Sponge UI interface",
Long: `open the Sponge UI interface.
Short: "Open the sponge UI interface",
Long: `open the sponge UI interface.
Examples:
# no log for running.

View File

@ -6,12 +6,12 @@ import (
"github.com/spf13/cobra"
)
// NewWebCommand web commands
func NewWebCommand() *cobra.Command {
// GenWebCommand generate web server code
func GenWebCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "web",
Short: "Generate model, cache, dao, handler, http codes",
Long: "generate model, cache, dao, handler, http codes.",
Short: "Generate model, cache, dao, handler, web codes",
Long: "generate model, cache, dao, handler, web codes.",
SilenceErrors: true,
SilenceUsage: true,
}

6
doc.go
View File

@ -9,15 +9,15 @@
//
// completion Generate the autocompletion script for the specified shell
// config Generate go config codes from yaml file
// del-omitempty Delete json tag omitempty
// help Help about any command
// init Initialize sponge
// merge Merge the generated code into the template file
// micro Generate proto, model, cache, dao, service, rpc, rpc-gw, rpc-cli codes
// run Open the Sponge UI interface
// patch Command set for patching server code
// run Open the sponge UI interface
// tools Managing sponge dependency tools
// upgrade Upgrade sponge to the latest version
// web Generate model, cache, dao, handler, http codes
// web Generate model, cache, dao, handler, web codes
//
// Flags:
//

13
scripts/copy-proto.sh Normal file
View File

@ -0,0 +1,13 @@
#!/bin/bash
serverDir=$1
function checkResult() {
result=$1
if [ ${result} -ne 0 ]; then
exit ${result}
fi
}
sponge patch copy-proto --server-dir=$serverDir
checkResult $?

View File

@ -2,7 +2,7 @@
patchType=$1
typesPb="types-pb"
mysqlInit="mysql-redis-init"
mysqlInit="mysql-init"
function checkResult() {
result=$1
@ -12,22 +12,22 @@ function checkResult() {
}
function patchTypesPb() {
sponge gen types-pb --out=./
sponge patch gen-types-pb --out=./
checkResult $?
}
function patchMysqlAndRedisInit() {
sponge gen mysql-redis-init --out=./
function patchMysqlInit() {
sponge patch gen-mysql-init --out=./
checkResult $?
}
if [ "X$patchType" = "X" ];then
patchTypesPb
patchMysqlAndRedisInit
patchMysqlInit
elif [ "$patchType" = "$typesPb" ]; then
patchTypesPb
elif [ "$patchType" = "$mysqlInit" ]; then
patchMysqlAndRedisInit
patchMysqlInit
else
echo "TYPE should be "", $typesPb or $mysqlInit."
exit 1

View File

@ -145,7 +145,7 @@ generateBySpecifiedProto
handlePbGoFiles $protoBasePath
# delete json tag omitempty
sponge del-omitempty --dir=$protoBasePath --suffix-name=pb.go > /dev/null
sponge patch del-omitempty --dir=$protoBasePath --suffix-name=pb.go > /dev/null
checkResult $?
go mod tidy