diff --git a/Makefile b/Makefile index d9534c9..dbfee0f 100644 --- a/Makefile +++ b/Makefile @@ -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: diff --git a/cmd/sponge/commands/gen.go b/cmd/sponge/commands/gen.go deleted file mode 100644 index a172399..0000000 --- a/cmd/sponge/commands/gen.go +++ /dev/null @@ -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 -} diff --git a/cmd/sponge/commands/merge/common.go b/cmd/sponge/commands/merge/common.go index 2b76243..060584f 100644 --- a/cmd/sponge/commands/merge/common.go +++ b/cmd/sponge/commands/merge/common.go @@ -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 { diff --git a/cmd/sponge/commands/micro.go b/cmd/sponge/commands/micro.go index 909d1db..83cc443 100644 --- a/cmd/sponge/commands/micro.go +++ b/cmd/sponge/commands/micro.go @@ -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", diff --git a/cmd/sponge/commands/patch.go b/cmd/sponge/commands/patch.go new file mode 100644 index 0000000..6b80ce2 --- /dev/null +++ b/cmd/sponge/commands/patch.go @@ -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 +} diff --git a/cmd/sponge/commands/patch/copy-proto.go b/cmd/sponge/commands/patch/copy-proto.go new file mode 100644 index 0000000..17b358b --- /dev/null +++ b/cmd/sponge/commands/patch/copy-proto.go @@ -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 +} diff --git a/cmd/sponge/commands/generate/del-omitempty.go b/cmd/sponge/commands/patch/del-omitempty.go similarity index 92% rename from cmd/sponge/commands/generate/del-omitempty.go rename to cmd/sponge/commands/patch/del-omitempty.go index aa1f1b1..c21bdc7 100644 --- a/cmd/sponge/commands/generate/del-omitempty.go +++ b/cmd/sponge/commands/patch/del-omitempty.go @@ -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, diff --git a/cmd/sponge/commands/generate/mysql-redis-init.go b/cmd/sponge/commands/patch/gen-mysql-init.go similarity index 73% rename from cmd/sponge/commands/generate/mysql-redis-init.go rename to cmd/sponge/commands/patch/gen-mysql-init.go index 99fd048..2130fb1 100644 --- a/cmd/sponge/commands/generate/mysql-redis-init.go +++ b/cmd/sponge/commands/patch/gen-mysql-init.go @@ -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] +} diff --git a/cmd/sponge/commands/generate/types-pb.go b/cmd/sponge/commands/patch/gen-types-pb.go similarity index 84% rename from cmd/sponge/commands/generate/types-pb.go rename to cmd/sponge/commands/patch/gen-types-pb.go index 5b3b36c..99c86a1 100644 --- a/cmd/sponge/commands/generate/types-pb.go +++ b/cmd/sponge/commands/patch/gen-types-pb.go @@ -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") } diff --git a/cmd/sponge/commands/root.go b/cmd/sponge/commands/root.go index 5c6d962..e42f0f4 100644 --- a/cmd/sponge/commands/root.go +++ b/cmd/sponge/commands/root.go @@ -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 diff --git a/cmd/sponge/commands/run.go b/cmd/sponge/commands/run.go index 28392dd..d292a7f 100644 --- a/cmd/sponge/commands/run.go +++ b/cmd/sponge/commands/run.go @@ -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. diff --git a/cmd/sponge/commands/web.go b/cmd/sponge/commands/web.go index 0e084e6..c3fc3b5 100644 --- a/cmd/sponge/commands/web.go +++ b/cmd/sponge/commands/web.go @@ -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, } diff --git a/doc.go b/doc.go index 71e01fa..6f0131b 100644 --- a/doc.go +++ b/doc.go @@ -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: // diff --git a/scripts/copy-proto.sh b/scripts/copy-proto.sh new file mode 100644 index 0000000..29eb1da --- /dev/null +++ b/scripts/copy-proto.sh @@ -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 $? diff --git a/scripts/patch.sh b/scripts/patch.sh index 6c8621f..17078c1 100644 --- a/scripts/patch.sh +++ b/scripts/patch.sh @@ -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 diff --git a/scripts/protoc.sh b/scripts/protoc.sh index 1bb0771..d3ec13a 100644 --- a/scripts/protoc.sh +++ b/scripts/protoc.sh @@ -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