adjust directory structure of large repository

This commit is contained in:
zhuyasen 2024-09-19 22:04:00 +08:00
parent 1907ce37d0
commit acdcb11599
49 changed files with 1256 additions and 779 deletions

View File

@ -3,9 +3,8 @@ syntax = "proto3";
package api.greeter.v1;
import "google/api/annotations.proto";
import "google/api/http.proto";
option go_package = "yourModuleName222/api/v1";
option go_package = "yourModuleName/api/v1";
service GreeterService {
// create a record
@ -23,6 +22,12 @@ service GreeterService {
};
}
rpc DeleteByID2(DeleteGreeterByIDRequest) returns (DeleteGreeterByIDReply) {
option (google.api.http) = {
delete: "/api/v1/greeter2/{id}"
};
}
// update a record by id
rpc UpdateByID(UpdateGreeterByIDRequest) returns (UpdateGreeterByIDReply) {
option (google.api.http) = {

View File

@ -4,7 +4,7 @@ package v1;
import "google/api/annotations.proto";
option go_package = "yourModuleName333/api/v1";
option go_package = "yourModuleName/api/v1";
service mixed {
// create a record

View File

@ -39,7 +39,7 @@ protoc --proto_path=. --proto_path=./third_party --go-gin_out=. --go-gin_opt=pat
protoc --proto_path=. --proto_path=./third_party --go-gin_out=. --go-gin_opt=paths=source_relative --go-gin_opt=plugin=mix \
--go-gin_opt=moduleName=yourModuleName --go-gin_opt=serverName=yourServerName *.proto
# if you want the generated code to suited to mono-repo, you need to specify the parameter --go-gin_opt=suitedMonoRepo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --go-gin_opt=suitedMonoRepo=true
Tip:
If you want to merge the code, after generating the code, execute the command "sponge merge http-pb" or
@ -89,7 +89,7 @@ func main() {
pluginName := strings.ReplaceAll(plugin, " ", "")
dirName := "internal"
if suitedMonoRepo {
dirName = "Internal"
dirName = serverName + "/internal"
}
switch pluginName {
case handlerPlugin:
@ -279,7 +279,7 @@ func firstLetterToUpper(s string) []byte {
func adaptMonoRepo(moduleName string, serverName string, data []byte) []byte {
matchStr := map[string]string{
fmt.Sprintf("\"%s/internal/", moduleName): fmt.Sprintf("\"%s/Internal/", moduleName+"/"+serverName),
fmt.Sprintf("\"%s/internal/", moduleName): fmt.Sprintf("\"%s/internal/", moduleName+"/"+serverName),
fmt.Sprintf("\"%s/configs", moduleName): fmt.Sprintf("\"%s/configs", moduleName+"/"+serverName),
fmt.Sprintf("\"%s/api", moduleName): fmt.Sprintf("\"%s/api", moduleName+"/"+serverName),
}

View File

@ -22,7 +22,7 @@ const (
protoc --proto_path=. --proto_path=./third_party --go-rpc-tmpl_out=. --go-rpc-tmpl_opt=paths=source_relative \
--go-rpc-tmpl_opt=moduleName=yourModuleName --go-rpc-tmpl_opt=serverName=yourServerName *.proto
# if you want the generated code to suited to mono-repo, you need to specify the parameter --go-gin_opt=suitedMonoRepo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --go-gin_opt=suitedMonoRepo=true
Tip:
If you want to merge the code, after generating the code, execute the command "sponge merge rpc-pb",
@ -72,7 +72,7 @@ func main() {
dirName := "internal"
if suitedMonoRepo {
dirName = "Internal"
dirName = serverName + "/internal"
}
if tmplDir == "" {
tmplDir = dirName + "/service"
@ -182,7 +182,7 @@ func firstLetterToUpper(s string) []byte {
func adaptMonoRepo(moduleName string, serverName string, data []byte) []byte {
matchStr := map[string]string{
fmt.Sprintf("\"%s/internal/", moduleName): fmt.Sprintf("\"%s/Internal/", moduleName+"/"+serverName),
fmt.Sprintf("\"%s/internal/", moduleName): fmt.Sprintf("\"%s/internal/", moduleName+"/"+serverName),
fmt.Sprintf("\"%s/configs", moduleName): fmt.Sprintf("\"%s/configs", moduleName+"/"+serverName),
fmt.Sprintf("\"%s/api", moduleName): fmt.Sprintf("\"%s/api", moduleName+"/"+serverName),
}

View File

@ -3,6 +3,7 @@ package generate
import (
"errors"
"fmt"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
@ -33,12 +34,12 @@ func CacheCommand(parentName string) *cobra.Command {
Examples:
# generate kv cache code
sponge %s cache --module-name=yourModuleName --cache-name=userToken --prefix-key=user:token: --key-name=id --key-type=uint64 --value-name=token --value-type=string
sponge %s cache --module-name=yourModuleName --cache-name=userToken --key-name=id --key-type=uint64 --value-name=token --value-type=string
# generate kv cache code and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge %s cache --module-name=yourModuleName --cache-name=token --prefix-key=user:token: --key-name=id --key-type=uint64 --value-name=token --value-type=string --out=./yourServerDir
sponge %s cache --module-name=yourModuleName --cache-name=token --prefix-key=user:token --key-name=id --key-type=uint64 --value-name=token --value-type=string --out=./yourServerDir
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true --serverName=yourServerName
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true --server-name=yourServerName
`, parentName, parentName)),
SilenceErrors: true,
SilenceUsage: true,
@ -58,6 +59,13 @@ Examples:
serverName = convertServerName(serverName)
outPath = changeOutPath(outPath, serverName)
}
cacheName = strings.ReplaceAll(cacheName, ":", "")
if prefixKey == "" || prefixKey == ":" {
prefixKey = cacheName + ":"
} else if prefixKey[len(prefixKey)-1] != ':' {
prefixKey += ":"
}
var err error
var g = &stringCacheGenerator{
@ -91,14 +99,14 @@ using help:
cmd.Flags().StringVarP(&moduleName, "module-name", "m", "", "module-name is the name of the module in the go.mod file")
cmd.Flags().StringVarP(&cacheName, "cache-name", "c", "", "cache name, e.g. userToken")
_ = cmd.MarkFlagRequired("cache-name")
cmd.Flags().StringVarP(&prefixKey, "prefix-key", "p", "", "cache prefix key, must end with a colon, e.g. user:token:")
cmd.Flags().StringVarP(&keyName, "key-name", "", "", "key name, e.g. id")
cmd.Flags().StringVarP(&prefixKey, "prefix-key", "p", "", "cache prefix key, e.g. user:token")
cmd.Flags().StringVarP(&keyName, "key-name", "k", "", "key name, e.g. id")
_ = cmd.MarkFlagRequired("key-name")
cmd.Flags().StringVarP(&keyType, "key-type", "", "", "key go type, e.g. uint64")
cmd.Flags().StringVarP(&keyType, "key-type", "t", "", "key go type, e.g. uint64")
_ = cmd.MarkFlagRequired("key-type")
cmd.Flags().StringVarP(&valueName, "value-name", "", "", "value name, e.g. token")
cmd.Flags().StringVarP(&valueName, "value-name", "v", "", "value name, e.g. token")
_ = cmd.MarkFlagRequired("value-name")
cmd.Flags().StringVarP(&valueType, "value-type", "", "", "value go type, e.g. string")
cmd.Flags().StringVarP(&valueType, "value-type", "w", "", "value go type, e.g. string")
_ = cmd.MarkFlagRequired("value-type")
cmd.Flags().StringVarP(&serverName, "server-name", "s", "", "server name")
cmd.Flags().BoolVarP(&suitedMonoRepo, "suited-mono-repo", "l", false, "whether the generated code is suitable for mono-repo")
@ -122,18 +130,12 @@ type stringCacheGenerator struct {
}
func (g *stringCacheGenerator) generateCode() (string, error) {
subTplName := "cache"
subTplName := codeNameCache
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
}
if g.prefixKey == "" || g.prefixKey == ":" {
g.prefixKey = g.cacheName + ":"
} else if g.prefixKey[len(g.prefixKey)-1] != ':' {
g.prefixKey += ":"
}
// specify the subdirectory and files
subDirs := []string{}
subFiles := []string{"internal/cache/cacheNameExample.go"}
@ -207,7 +209,7 @@ func (g *stringCacheGenerator) addFields(r replacer.Replacer) []replacer.Field {
}...)
if g.suitedMonoRepo {
fs := SubServerCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := SubServerCodeFields(g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -11,6 +11,7 @@ import (
"regexp"
"strconv"
"strings"
"time"
"github.com/huandu/xstrings"
@ -45,7 +46,7 @@ const (
codeNameHandler = "handler"
codeNameHandlerPb = "handler-pb"
codeNameService = "service"
codeNameServiceHTTP = "service-http"
codeNameServiceHTTP = "service-handler"
codeNameHTTP = "http"
codeNameHTTPPb = "http-pb"
codeNameGRPC = "grpc"
@ -376,7 +377,12 @@ func getNamesFromOutDir(dir string) (moduleName string, serverName string, suite
return "", "", false
}
func saveProtobufFiles(moduleName string, serverName string, outputDir string, protobufFiles []string) error {
func saveProtobufFiles(moduleName string, serverName string, suitedMonoRepo bool, outputDir string, protobufFiles []string) error {
if suitedMonoRepo {
outputDir = strings.TrimSuffix(outputDir, serverName)
outputDir = strings.TrimSuffix(outputDir, gofile.GetPathDelimiter())
}
for _, pbFile := range protobufFiles {
pbContent, err := os.ReadFile(pbFile)
if err != nil {
@ -390,6 +396,9 @@ func saveProtobufFiles(moduleName string, serverName string, outputDir string, p
_, name := filepath.Split(pbFile)
file := dir + "/" + name
if gofile.IsExists(file) {
return fmt.Errorf("file %s already exists", file)
}
err = os.WriteFile(file, pbContent, 0666)
if err != nil {
return fmt.Errorf("save file %s error, %v", file, err)
@ -607,21 +616,51 @@ func removeElements(slice []string, elements ...string) []string {
return result
}
func serverCodeFields(outDir string, moduleName string, serverName string) []replacer.Field {
parts := strings.Split(outDir, gofile.GetPathDelimiter())
func moveProtoFileToAPIDir(moduleName string, serverName string, suitedMonoRepo bool, outputDir string) error {
apiDir := outputDir + gofile.GetPathDelimiter() + "api"
protoFiles, _ := gofile.ListFiles(apiDir, gofile.WithNoAbsolutePath(), gofile.WithSuffix(".proto"))
if err := saveProtobufFiles(moduleName, serverName, suitedMonoRepo, outputDir, protoFiles); err != nil {
return err
}
time.Sleep(time.Millisecond * 100)
_ = os.RemoveAll(apiDir)
return nil
}
var (
// for protoc.sh and protoc-doc.sh
monoRepoAPIPath = `bash scripts/init.sh
cd ..
protoBasePath="api"`
// for patch.sh
typePbShellCode = ` if [ ! -d "../api/types" ]; then
sponge patch gen-types-pb --out=./
checkResult $?
mv -f api/types ../api
rmdir api
fi`
dupCodeMark = "--dir=internal/ecode"
adaptDupCode = func(serverType string, serverName string) string {
if serverType == codeNameHTTP {
return dupCodeMark
}
return fmt.Sprintf("--dir=%s/internal/ecode", serverName)
}
)
func serverCodeFields(serverType string, moduleName string, serverName string) []replacer.Field {
return []replacer.Field{
{ // internal initial capital means exportable, external code can be referenced
{
Old: fmt.Sprintf("\"%s/internal/", moduleName),
New: fmt.Sprintf("\"%s/Internal/", moduleName+"/"+serverName),
New: fmt.Sprintf("\"%s/internal/", moduleName+"/"+serverName),
},
{
Old: parts[len(parts)-1] + gofile.GetPathDelimiter() + "internal",
New: parts[len(parts)-1] + gofile.GetPathDelimiter() + "Internal",
Old: "=$(cat docs/gen.info",
New: fmt.Sprintf("=$(cat %s/docs/gen.info", serverName),
},
{
Old: "--dir=internal/ecode",
New: "--dir=Internal/ecode",
Old: dupCodeMark,
New: adaptDupCode(serverType, serverName),
},
{
Old: fmt.Sprintf("\"%s/cmd/", moduleName),
@ -640,24 +679,50 @@ func serverCodeFields(outDir string, moduleName string, serverName string) []rep
New: fmt.Sprintf("\"%s/api", moduleName+"/"+serverName),
},
{
Old: "vrf internal",
New: "vrf Internal",
Old: "merge_file_name=docs/apis.json",
New: fmt.Sprintf("merge_file_name=%s/docs/apis.json", serverName),
},
{
Old: "--file=docs/apis.swagger.json",
New: fmt.Sprintf("--file=%s/docs/apis.swagger.json", serverName),
},
{
Old: "sponge merge http-pb",
New: fmt.Sprintf("sponge merge http-pb --dir=%s", serverName),
},
{
Old: "sponge merge rpc-pb",
New: fmt.Sprintf("sponge merge rpc-pb --dir=%s", serverName),
},
{
Old: "sponge merge rpc-gw-pb",
New: fmt.Sprintf("sponge merge rpc-gw-pb --dir=%s", serverName),
},
{
Old: "docs/apis.html",
New: fmt.Sprintf("%s/docs/apis.html", serverName),
},
{
Old: `sponge patch gen-types-pb --out=./`,
New: typePbShellCode,
},
{
Old: `protoBasePath="api"`,
New: monoRepoAPIPath,
},
{
Old: fmt.Sprintf("go get %s@", moduleName),
New: fmt.Sprintf("go get %s@", "github.com/zhufuyi/sponge"),
},
}
}
// SubServerCodeFields sub server code fields
func SubServerCodeFields(outDir string, moduleName string, serverName string) []replacer.Field {
parts := strings.Split(outDir, gofile.GetPathDelimiter())
func SubServerCodeFields(moduleName string, serverName string) []replacer.Field {
return []replacer.Field{
{ // internal initial capital means exportable, external code can be referenced
Old: fmt.Sprintf("\"%s/internal/", moduleName),
New: fmt.Sprintf("\"%s/Internal/", moduleName+"/"+serverName),
},
{
Old: parts[len(parts)-1] + gofile.GetPathDelimiter() + "internal",
New: parts[len(parts)-1] + gofile.GetPathDelimiter() + "Internal",
Old: fmt.Sprintf("\"%s/internal/", moduleName),
New: fmt.Sprintf("\"%s/internal/", moduleName+"/"+serverName),
},
{
Old: fmt.Sprintf("\"%s/configs", moduleName),
@ -667,10 +732,6 @@ func SubServerCodeFields(outDir string, moduleName string, serverName string) []
Old: fmt.Sprintf("\"%s/api", moduleName),
New: fmt.Sprintf("\"%s/api", moduleName+"/"+serverName),
},
{
Old: "vrf internal",
New: "vrf Internal",
},
}
}
@ -736,3 +797,49 @@ func cutPath(srcFilePath string) string {
srcFilePath = strings.ReplaceAll(srcFilePath, dirPath, ".")
return strings.ReplaceAll(srcFilePath, "\\", "/")
}
func wrapPoint(s string) string {
return "`" + s + "`"
}
func setReadmeTitle(moduleName string, serverName string, serverType string, suitedMonoRepo bool) string {
var repoType string
if suitedMonoRepo {
repoType = "mono-repo"
} else {
if serverType == codeNameHTTP {
repoType = "monolith"
} else {
repoType = "multi-repo"
}
}
return wellPrefix + serverName + fmt.Sprintf(`
| Feature | Value |
| :----------------: | :-----------: |
| Server name | %s |
| Server type | %s |
| Go module name | %s |
| Repository type | %s |
`, wrapPoint(serverName), wrapPoint(serverType), wrapPoint(moduleName), wrapPoint(repoType))
}
// GetGoModFields get go mod fields
func GetGoModFields(moduleName string) []replacer.Field {
return []replacer.Field{
{
Old: "github.com/zhufuyi/sponge",
New: moduleName,
},
{
Old: defaultGoModVersion,
New: getLocalGoVersion(),
},
{
Old: spongeTemplateVersionMark,
New: getLocalSpongeTemplateVersion(),
},
}
}

View File

@ -49,7 +49,7 @@ Examples:
# generate dao code and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge %s dao --db-driver=mysql --db-dsn=root:123456@(192.168.3.37:3306)/test --db-table=user --out=./yourServerDir
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true --serverName=yourServerName
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true --server-name=yourServerName
`, parentName, parentName, parentName, parentName)),
SilenceErrors: true,
SilenceUsage: true,
@ -152,7 +152,7 @@ type daoGenerator struct {
}
func (g *daoGenerator) generateCode() (string, error) {
subTplName := "dao"
subTplName := codeNameDao
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("r is nil")
@ -262,7 +262,7 @@ func (g *daoGenerator) addFields(r replacer.Replacer) []replacer.Field {
}...)
if g.suitedMonoRepo {
fs := SubServerCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := SubServerCodeFields(g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -39,7 +39,7 @@ Examples:
# generate grpc service code and specify the docker image repository address.
sponge micro grpc-http-pb --module-name=yourModuleName --server-name=yourServerName --project-name=yourProjectName --repo-addr=192.168.3.37:9443/user-name --protobuf-file=./demo.proto
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -106,7 +106,7 @@ func (g *httpAndGRPCPbGenerator) generateCode() error {
return err
}
subTplName := "grpc-http-pb"
subTplName := codeNameGRPCHTTP
r := Replacers[TplNameSponge]
if r == nil {
return errors.New("replacer is nil")
@ -121,9 +121,7 @@ func (g *httpAndGRPCPbGenerator) generateCode() error {
"sponge/.gitignore", "sponge/.golangci.yml", "sponge/go.mod", "sponge/go.sum",
"sponge/Jenkinsfile", "sponge/Makefile", "sponge/README.md",
}
if g.suitedMonoRepo {
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum")
}
if isImportTypes {
subFiles = append(subFiles, "api/types/types.proto")
}
@ -148,6 +146,12 @@ func (g *httpAndGRPCPbGenerator) generateCode() error {
"service.go", "service_test.go",
},
}
if g.suitedMonoRepo {
subDirs = removeElements(subDirs, "sponge/third_party")
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum", "api/types/types.proto")
}
replaceFiles := make(map[string][]string)
subFiles = append(subFiles, getSubFiles(selectFiles, replaceFiles)...)
@ -163,7 +167,9 @@ func (g *httpAndGRPCPbGenerator) generateCode() error {
return err
}
_ = saveProtobufFiles(g.moduleName, g.serverName, r.GetOutputDir(), protobufFiles)
if err = saveProtobufFiles(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir(), protobufFiles); err != nil {
return err
}
_ = saveGenInfo(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir())
fmt.Printf(`
@ -197,7 +203,8 @@ func (g *httpAndGRPCPbGenerator) addFields(r replacer.Replacer) []replacer.Field
fields = append(fields, deleteAllFieldsMark(r, protoShellFile, wellStartMark, wellEndMark)...)
fields = append(fields, deleteAllFieldsMark(r, appConfigFile, wellStartMark, wellEndMark)...)
//fields = append(fields, deleteFieldsMark(r, deploymentConfigFile, wellStartMark, wellEndMark)...)
fields = append(fields, replaceFileContentMark(r, readmeFile, wellPrefix+g.serverName)...)
fields = append(fields, replaceFileContentMark(r, readmeFile,
setReadmeTitle(g.moduleName, g.serverName, codeNameGRPCHTTP, g.suitedMonoRepo))...)
fields = append(fields, []replacer.Field{
{ // replace the configuration of the *.yml file
Old: appConfigFileMark,

View File

@ -50,6 +50,8 @@ Examples:
# generate handler and protobuf code and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge web handler-pb --db-driver=mysql --db-dsn=root:123456@(192.168.3.37:3306)/test --db-table=user --out=./yourServerDir
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -148,7 +150,7 @@ type handlerPbGenerator struct {
}
func (g *handlerPbGenerator) generateCode() (string, error) {
subTplName := "handler-pb"
subTplName := codeNameHandlerPb
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
@ -226,6 +228,12 @@ func (g *handlerPbGenerator) generateCode() (string, error) {
return "", err
}
if g.suitedMonoRepo {
if err := moveProtoFileToAPIDir(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir()); err != nil {
return "", err
}
}
return r.GetOutputDir(), nil
}
@ -319,7 +327,7 @@ func (g *handlerPbGenerator) addFields(r replacer.Replacer) []replacer.Field {
}...)
if g.suitedMonoRepo {
fs := SubServerCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := SubServerCodeFields(g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -49,7 +49,7 @@ Examples:
# generate handler code and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge web handler --db-driver=mysql --db-dsn=root:123456@(192.168.3.37:3306)/test --db-table=user --out=./yourServerDir
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true --serverName=yourServerName
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true --server-name=yourServerName
`),
SilenceErrors: true,
SilenceUsage: true,
@ -146,7 +146,7 @@ type handlerGenerator struct {
}
func (g *handlerGenerator) generateCode() (string, error) {
subTplName := "handler"
subTplName := codeNameHandler
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
@ -284,7 +284,7 @@ func (g *handlerGenerator) addFields(r replacer.Replacer) []replacer.Field {
}...)
if g.suitedMonoRepo {
fs := SubServerCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := SubServerCodeFields(g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -39,7 +39,7 @@ Examples:
# generate web service code and specify the docker image repository address.
sponge web http-pb --module-name=yourModuleName --server-name=yourServerName --project-name=yourProjectName --repo-addr=192.168.3.37:9443/user-name --protobuf-file=./test.proto
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -106,7 +106,7 @@ func (g *httpPbGenerator) generateCode() (string, error) {
return "", err
}
subTplName := "http-pb"
subTplName := codeNameHTTPPb
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
@ -121,9 +121,7 @@ func (g *httpPbGenerator) generateCode() (string, error) {
"sponge/.gitignore", "sponge/.golangci.yml", "sponge/go.mod", "sponge/go.sum",
"sponge/Jenkinsfile", "sponge/Makefile", "sponge/README.md",
}
if g.suitedMonoRepo {
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum")
}
if isImportTypes {
subFiles = append(subFiles, "api/types/types.proto")
}
@ -145,8 +143,13 @@ func (g *httpPbGenerator) generateCode() (string, error) {
"http.go", "http_test.go", "http_option.go",
},
}
replaceFiles := make(map[string][]string)
if g.suitedMonoRepo {
subDirs = removeElements(subDirs, "sponge/third_party")
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum", "api/types/types.proto")
}
replaceFiles := make(map[string][]string)
subFiles = append(subFiles, getSubFiles(selectFiles, replaceFiles)...)
// ignore some directories
@ -161,7 +164,9 @@ func (g *httpPbGenerator) generateCode() (string, error) {
return "", err
}
_ = saveProtobufFiles(g.moduleName, g.serverName, r.GetOutputDir(), protobufFiles)
if err = saveProtobufFiles(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir(), protobufFiles); err != nil {
return "", err
}
_ = saveGenInfo(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir())
_ = saveEmptySwaggerJSON(r.GetOutputDir())
@ -196,7 +201,8 @@ func (g *httpPbGenerator) addFields(r replacer.Replacer) []replacer.Field {
fields = append(fields, deleteAllFieldsMark(r, protoShellFile, wellStartMark, wellEndMark)...)
fields = append(fields, deleteAllFieldsMark(r, appConfigFile, wellStartMark, wellEndMark)...)
//fields = append(fields, deleteFieldsMark(r, deploymentConfigFile, wellStartMark, wellEndMark)...)
fields = append(fields, replaceFileContentMark(r, readmeFile, wellPrefix+g.serverName)...)
fields = append(fields, replaceFileContentMark(r, readmeFile,
setReadmeTitle(g.moduleName, g.serverName, codeNameHTTPPb, g.suitedMonoRepo))...)
fields = append(fields, []replacer.Field{
{ // replace the configuration of the *.yml file
Old: appConfigFileMark,

View File

@ -55,7 +55,7 @@ Examples:
# generate web service code and specify the docker image repository address.
sponge web http --module-name=yourModuleName --server-name=yourServerName --project-name=yourProjectName --repo-addr=192.168.3.37:9443/user-name --db-driver=mysql --db-dsn=root:123456@(192.168.3.37:3306)/test --db-table=user
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -99,6 +99,7 @@ Examples:
codes: codes,
outPath: outPath,
isExtendedAPI: sqlArgs.IsExtendedAPI,
serverType: codeNameHTTP,
suitedMonoRepo: suitedMonoRepo,
}
@ -182,12 +183,13 @@ type httpGenerator struct {
isEmbed bool
isExtendedAPI bool
suitedMonoRepo bool
serverType string
fields []replacer.Field
}
func (g *httpGenerator) generateCode() (string, error) {
subTplName := "http"
subTplName := codeNameHTTP
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
@ -323,7 +325,8 @@ func (g *httpGenerator) addFields(r replacer.Replacer) []replacer.Field {
fields = append(fields, deleteAllFieldsMark(r, protoShellFile, wellStartMark, wellEndMark)...)
fields = append(fields, deleteAllFieldsMark(r, appConfigFile, wellStartMark, wellEndMark)...)
//fields = append(fields, deleteFieldsMark(r, deploymentConfigFile, wellStartMark, wellEndMark)...)
fields = append(fields, replaceFileContentMark(r, readmeFile, wellPrefix+g.serverName)...)
fields = append(fields, replaceFileContentMark(r, readmeFile,
setReadmeTitle(g.moduleName, g.serverName, codeNameHTTP, g.suitedMonoRepo))...)
fields = append(fields, []replacer.Field{
{ // replace the configuration of the *.yml file
Old: appConfigFileMark,
@ -495,7 +498,7 @@ func (g *httpGenerator) addFields(r replacer.Replacer) []replacer.Field {
}...)
if g.suitedMonoRepo {
fs := serverCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := serverCodeFields(g.serverType, g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -97,7 +97,7 @@ type modelGenerator struct {
}
func (g *modelGenerator) generateCode() (string, error) {
subTplName := "model"
subTplName := codeNameModel
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")

View File

@ -128,7 +128,7 @@ type protobufGenerator struct {
}
func (g *protobufGenerator) generateCode() (string, error) {
subTplName := "protobuf"
subTplName := codeNameProtobuf
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")

View File

@ -37,7 +37,7 @@ Examples:
# generate grpc connection code and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge micro rpc-conn --rpc-server-name=user --out=./yourServerDir
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true --serverName=yourServerName
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true --server-name=yourServerName
`),
SilenceErrors: true,
SilenceUsage: true,
@ -109,7 +109,7 @@ type grpcConnectionGenerator struct {
}
func (g *grpcConnectionGenerator) generateCode() (string, error) {
subTplName := "rpc-conn"
subTplName := codeNameGRPCConn
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
@ -121,7 +121,7 @@ func (g *grpcConnectionGenerator) generateCode() (string, error) {
r.SetSubDirsAndFiles(subDirs, subFiles...)
_ = r.SetOutputDir(g.outPath, subTplName)
fields := g.addFields(r)
fields := g.addFields()
r.SetReplacementFields(fields)
if err := r.SaveFiles(); err != nil {
return "", err
@ -130,7 +130,7 @@ func (g *grpcConnectionGenerator) generateCode() (string, error) {
return r.GetOutputDir(), nil
}
func (g *grpcConnectionGenerator) addFields(r replacer.Replacer) []replacer.Field {
func (g *grpcConnectionGenerator) addFields() []replacer.Field {
var fields []replacer.Field
fields = append(fields, []replacer.Field{
@ -150,7 +150,7 @@ func (g *grpcConnectionGenerator) addFields(r replacer.Replacer) []replacer.Fiel
}...)
if g.suitedMonoRepo {
fs := SubServerCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := SubServerCodeFields(g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -39,7 +39,7 @@ Examples:
# generate grpc gateway service code and specify the docker image repository address.
sponge micro rpc-gw-pb --module-name=yourModuleName --server-name=yourServerName --project-name=yourProjectName --repo-addr=192.168.3.37:9443/user-name --protobuf-file=./demo.proto
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -106,7 +106,7 @@ func (g *rpcGwPbGenerator) generateCode() error {
return err
}
subTplName := "rpc-gw-pb"
subTplName := codeNameGRPCGW
r := Replacers[TplNameSponge]
if r == nil {
return errors.New("replacer is nil")
@ -123,9 +123,6 @@ func (g *rpcGwPbGenerator) generateCode() error {
"sponge/Jenkinsfile", "sponge/Makefile", "sponge/README.md",
}
if g.suitedMonoRepo {
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum")
}
if isImportTypes {
subFiles = append(subFiles, "api/types/types.proto")
}
@ -147,8 +144,13 @@ func (g *rpcGwPbGenerator) generateCode() error {
"http.go", "http_test.go", "http_option.go",
},
}
replaceFiles := make(map[string][]string)
if g.suitedMonoRepo {
subDirs = removeElements(subDirs, "sponge/third_party")
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum", "api/types/types.proto")
}
replaceFiles := make(map[string][]string)
subFiles = append(subFiles, getSubFiles(selectFiles, replaceFiles)...)
// ignore some directories
@ -163,7 +165,9 @@ func (g *rpcGwPbGenerator) generateCode() error {
return err
}
_ = saveProtobufFiles(g.moduleName, g.serverName, r.GetOutputDir(), protobufFiles)
if err = saveProtobufFiles(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir(), protobufFiles); err != nil {
return err
}
_ = saveGenInfo(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir())
_ = saveEmptySwaggerJSON(r.GetOutputDir())
@ -198,7 +202,8 @@ func (g *rpcGwPbGenerator) addFields(r replacer.Replacer) []replacer.Field {
fields = append(fields, deleteAllFieldsMark(r, protoShellFile, wellStartMark, wellEndMark)...)
fields = append(fields, deleteAllFieldsMark(r, appConfigFile, wellStartMark, wellEndMark)...)
//fields = append(fields, deleteFieldsMark(r, deploymentConfigFile, wellStartMark, wellEndMark)...)
fields = append(fields, replaceFileContentMark(r, readmeFile, wellPrefix+g.serverName)...)
fields = append(fields, replaceFileContentMark(r, readmeFile,
setReadmeTitle(g.moduleName, g.serverName, codeNameGRPCGW, g.suitedMonoRepo))...)
fields = append(fields, []replacer.Field{
{ // replace the configuration of the *.yml file
Old: appConfigFileMark,

View File

@ -39,7 +39,7 @@ Examples:
# generate grpc service code and specify the docker image repository address.
sponge micro rpc-pb --module-name=yourModuleName --server-name=yourServerName --project-name=yourProjectName --repo-addr=192.168.3.37:9443/user-name --protobuf-file=./demo.proto
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -107,7 +107,7 @@ func (g *rpcPbGenerator) generateCode() error {
return err
}
subTplName := "rpc-pb"
subTplName := codeNameGRPCPb
r := Replacers[TplNameSponge]
if r == nil {
return errors.New("replacer is nil")
@ -123,9 +123,6 @@ func (g *rpcPbGenerator) generateCode() error {
"sponge/Jenkinsfile", "sponge/Makefile", "sponge/README.md",
}
if g.suitedMonoRepo {
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum")
}
if isImportTypes {
subFiles = append(subFiles, "api/types/types.proto")
}
@ -144,6 +141,12 @@ func (g *rpcPbGenerator) generateCode() error {
"service.go", "service_test.go",
},
}
if g.suitedMonoRepo {
subDirs = removeElements(subDirs, "sponge/third_party")
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum", "api/types/types.proto")
}
replaceFiles := make(map[string][]string)
subFiles = append(subFiles, getSubFiles(selectFiles, replaceFiles)...)
@ -161,7 +164,9 @@ func (g *rpcPbGenerator) generateCode() error {
return err
}
_ = saveProtobufFiles(g.moduleName, g.serverName, r.GetOutputDir(), protobufFiles)
if err = saveProtobufFiles(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir(), protobufFiles); err != nil {
return err
}
_ = saveGenInfo(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir())
fmt.Printf(`
@ -193,7 +198,8 @@ func (g *rpcPbGenerator) addFields(r replacer.Replacer) []replacer.Field {
fields = append(fields, deleteAllFieldsMark(r, protoShellFile, wellStartMark, wellEndMark)...)
fields = append(fields, deleteAllFieldsMark(r, appConfigFile, wellStartMark, wellEndMark)...)
//fields = append(fields, deleteFieldsMark(r, deploymentConfigFile, wellStartMark, wellEndMark)...)
fields = append(fields, replaceFileContentMark(r, readmeFile, wellPrefix+g.serverName)...)
fields = append(fields, replaceFileContentMark(r, readmeFile,
setReadmeTitle(g.moduleName, g.serverName, codeNameGRPCPb, g.suitedMonoRepo))...)
fields = append(fields, []replacer.Field{
{ // replace the configuration of the *.yml file
Old: appConfigFileMark,

View File

@ -56,7 +56,7 @@ Examples:
# generate grpc service code and specify the docker image repository address.
sponge micro rpc --module-name=yourModuleName --server-name=yourServerName --project-name=yourProjectName --repo-addr=192.168.3.37:9443/user-name --db-driver=mysql --db-dsn=root:123456@(192.168.3.37:3306)/test --db-table=user
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -187,7 +187,7 @@ type rpcGenerator struct {
}
func (g *rpcGenerator) generateCode() (string, error) {
subTplName := "rpc"
subTplName := codeNameGRPC
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
@ -203,10 +203,6 @@ func (g *rpcGenerator) generateCode() (string, error) {
"sponge/Jenkinsfile", "sponge/Makefile", "sponge/README.md",
}
if g.suitedMonoRepo {
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum")
}
selectFiles := map[string][]string{
"api/serverNameExample/v1": {
"userExample.proto",
@ -236,8 +232,14 @@ func (g *rpcGenerator) generateCode() (string, error) {
"service.go", "service_test.go", "userExample.go", "userExample_client_test.go",
},
}
replaceFiles := make(map[string][]string)
if g.suitedMonoRepo {
subDirs = removeElements(subDirs, "sponge/third_party")
subFiles = removeElements(subFiles, "sponge/go.mod", "sponge/go.sum")
delete(selectFiles, "api/types")
}
replaceFiles := make(map[string][]string)
switch strings.ToLower(g.dbDriver) {
case DBDriverMysql, DBDriverPostgresql, DBDriverTidb, DBDriverSqlite:
g.fields = append(g.fields, getExpectedSQLForDeletionField(g.isEmbed)...)
@ -289,6 +291,18 @@ func (g *rpcGenerator) generateCode() (string, error) {
if err := r.SaveFiles(); err != nil {
return "", err
}
if g.suitedMonoRepo {
if err := moveProtoFileToAPIDir(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir()); err != nil {
return "", err
}
//apiDir := g.serverName + gofile.GetPathDelimiter() + "api"
//protoFiles, _ := gofile.ListFiles(apiDir, gofile.WithNoAbsolutePath(), gofile.WithSuffix(".proto"))
//if err := saveProtobufFiles(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir(), protoFiles); err != nil {
// return "", err
//}
//_ = os.RemoveAll(apiDir)
}
_ = saveGenInfo(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir())
return r.GetOutputDir(), nil
@ -321,7 +335,8 @@ func (g *rpcGenerator) addFields(r replacer.Replacer) []replacer.Field {
fields = append(fields, deleteAllFieldsMark(r, protoShellFile, wellStartMark, wellEndMark)...)
fields = append(fields, deleteAllFieldsMark(r, appConfigFile, wellStartMark, wellEndMark)...)
//fields = append(fields, deleteFieldsMark(r, deploymentConfigFile, wellStartMark, wellEndMark)...)
fields = append(fields, replaceFileContentMark(r, readmeFile, wellPrefix+g.serverName)...)
fields = append(fields, replaceFileContentMark(r, readmeFile,
setReadmeTitle(g.moduleName, g.serverName, codeNameGRPC, g.suitedMonoRepo))...)
fields = append(fields, []replacer.Field{
{ // replace the configuration of the *.yml file
Old: appConfigFileMark,
@ -343,7 +358,7 @@ func (g *rpcGenerator) addFields(r replacer.Replacer) []replacer.Field {
Old: daoFileMark,
New: g.codes[parser.CodeTypeDAO],
},
{ // replace the contents of the handler/userExample_logic.go file
{ // replace the contents of the service/userExample.go file
Old: embedTimeMark,
New: getEmbedTimeCode(g.isEmbed),
},

View File

@ -51,7 +51,7 @@ Examples:
# generate service and handler code and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge micro service-handler --db-driver=mysql --db-dsn=root:123456@(192.168.3.37:3306)/test --db-table=user --out=./yourServerDir
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -152,7 +152,7 @@ type serviceAndHandlerGenerator struct {
// nolint
func (g *serviceAndHandlerGenerator) generateCode() (string, error) {
subTplName := "service-handler"
subTplName := codeNameServiceHTTP
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
@ -231,6 +231,12 @@ func (g *serviceAndHandlerGenerator) generateCode() (string, error) {
return "", err
}
if g.suitedMonoRepo {
if err := moveProtoFileToAPIDir(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir()); err != nil {
return "", err
}
}
return r.GetOutputDir(), nil
}
@ -338,7 +344,7 @@ func (g *serviceAndHandlerGenerator) addFields(r replacer.Replacer) []replacer.F
}...)
if g.suitedMonoRepo {
fs := SubServerCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := SubServerCodeFields(g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -50,7 +50,7 @@ Examples:
# generate service code and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
sponge micro service --db-driver=mysql --db-dsn=root:123456@(192.168.3.37:3306)/test --db-table=user --out=./yourServerDir
# if you want the generated code to suited to mono-repo, you need to specify the parameter --suited-mono-repo=true
# if you want the generated code to suited to mono-repo, you need to set the parameter --suited-mono-repo=true
`),
SilenceErrors: true,
SilenceUsage: true,
@ -150,7 +150,7 @@ type serviceGenerator struct {
// nolint
func (g *serviceGenerator) generateCode() (string, error) {
subTplName := "service"
subTplName := codeNameService
r := Replacers[TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
@ -229,6 +229,12 @@ func (g *serviceGenerator) generateCode() (string, error) {
return "", err
}
if g.suitedMonoRepo {
if err := moveProtoFileToAPIDir(g.moduleName, g.serverName, g.suitedMonoRepo, r.GetOutputDir()); err != nil {
return "", err
}
}
return r.GetOutputDir(), nil
}
@ -315,7 +321,7 @@ func (g *serviceGenerator) addFields(r replacer.Replacer) []replacer.Field {
}...)
if g.suitedMonoRepo {
fs := SubServerCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := SubServerCodeFields(g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -267,13 +267,8 @@ func NewCenter(configFile string) (*Center, error) {
sponge merge rpc-pb
checkResult $?
colorCyan='\033[1;36m'
highBright='\033[1m'
markEnd='\033[0m'
echo ""
echo -e "${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then test grpc api in the file ${colorCyan}internal/service/xxx_client_test.go${markEnd}."
echo ""`
tipMsg="${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then test grpc api in the file ${colorCyan}internal/service/xxx_client_test.go${markEnd}."
`
// for http-pb
protoShellHandlerCode = `
@ -301,13 +296,8 @@ func NewCenter(configFile string) (*Center, error) {
sponge merge http-pb
checkResult $?
colorCyan='\033[1;36m'
highBright='\033[1m'
markEnd='\033[0m'
echo ""
echo -e "${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then visit ${colorCyan}http://localhost:8080/apis/swagger/index.html${markEnd} in your browser. "
echo ""`
tipMsg="${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then visit ${colorCyan}http://localhost:8080/apis/swagger/index.html${markEnd} in your browser. "
`
// for rpc-gw
protoShellServiceCode = `
@ -335,15 +325,10 @@ func NewCenter(configFile string) (*Center, error) {
sponge merge rpc-gw-pb
checkResult $?
colorCyan='\033[1;36m'
highBright='\033[1m'
markEnd='\033[0m'
tipMsg="${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then visit ${colorCyan}http://localhost:8080/apis/swagger/index.html${markEnd} in your browser."
`
echo ""
echo -e "${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then visit ${colorCyan}http://localhost:8080/apis/swagger/index.html${markEnd} in your browser."
echo ""`
// for grpc-http
//nolint for grpc-http
protoShellServiceAndHandlerCode = `
# generate the swagger document and merge all files into docs/apis.swagger.json
protoc --proto_path=. --proto_path=./third_party \
@ -379,15 +364,8 @@ func NewCenter(configFile string) (*Center, error) {
sponge merge http-pb
checkResult $?
colorCyan='\033[1;36m'
highBright='\033[1m'
markEnd='\033[0m'
echo ""
echo -e "${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then"
echo -e " 1. test http api in your browser ${colorCyan}http://localhost:8080/apis/swagger/index.html${markEnd}"
echo -e " 2. test grpc api in the file ${colorCyan}internal/service/xxx_client_test.go${markEnd}"
echo ""`
tipMsg="${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then\n 1. test http api in your browser ${colorCyan}http://localhost:8080/apis/swagger/index.html${markEnd}\n 2. test grpc api in the file ${colorCyan}internal/service/xxx_client_test.go${markEnd}"
`
httpServerConfigCode = `# http server settings
http:

View File

@ -0,0 +1,125 @@
## English | [简体中文](readme-cn.md)
## Merge Command
The merge command is used to automatically merge the generated code into existing template files without affecting the existing business logic code. If any issues occur during the merging process, a backup of the code before merging will be saved in the `/tmp/sponge_merge_backup_code` directory, allowing you to restore the previous state of your code.
Manual merging of code is required when automatic merging fails due to changes in the number of services in the proto file.
<br>
### Manual Code Merging Instructions
In most cases, a proto file typically defines a single service. However, during development, the number of services in a proto file might change, such as increasing from one service to multiple services or decreasing from multiple services to one. Such changes may cause automatic merging to fail, necessitating manual code merging.
#### Manual Merging When Adding a Service
Let's take `greeter.proto` as an example, where the initial file contains a single service named `Foobar1`. The content of the file is as follows:
```protobuf
syntax = "proto3";
package greeter;
option go_package = "greeter";
service Foobar1 {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
```
When there is only one service, the automatic code merge usually works fine without manual intervention.
Suppose you need to add a new service named `Foobar2` to `greeter.proto`. The updated file content is as follows:
```protobuf
syntax = "proto3";
package greeter;
option go_package = "greeter";
service Foobar1 {
rpc SayHello (SayHelloRequest) returns (SayHelloReply) {}
}
message SayHelloRequest {
string name = 1;
}
message SayHelloReply {
string message = 1;
}
service Foobar2 {
rpc SayHello (SayHelloRequest) returns (SayHelloReply) {}
}
```
After adding a new service to the proto file, the automatic code merge may fail, requiring manual code merging. The steps for manual merging are as follows:
1. Based on the error message, locate the generated code file (with a suffix format of `.go.gen<timestamp>`) and open the file.
2. Find the Go code block corresponding to the `Foobar2` service and copy it into the target Go file (the one with the same prefix as the `.go.gen<timestamp>` file).
3. The Go file after copying must meet the following requirements:
- The number of service code blocks in the Go file must match the number of services in the proto file, and their order must be consistent.
- The service code blocks in the Go file must be separated by a fixed marker: `// ---------- Do not delete or move this split line, this is the merge code marker ----------`.
- If the proto file contains only one service, the service code block in the Go file does not need a separator.
By manually merging the code, the automatic merging feature will work correctly if the number of services in the proto file remains unchanged in the future.
<br>
#### Manual Merging When Removing a Service
Continuing with the `greeter.proto` example, the file currently contains two services: `Foobar1` and `Foobar2`. In this case, automatic code merging usually works fine without manual intervention.
Suppose you need to remove `Foobar2`, and the updated `greeter.proto` file is as follows:
```protobuf
syntax = "proto3";
package greeter;
option go_package = "greeter";
service Foobar1 {
rpc SayHello (SayHelloRequest) returns (SayHelloReply) {}
}
message SayHelloRequest {
string name = 1;
}
message SayHelloReply {
string message = 1;
}
```
At this point, automatic code merging may fail, requiring manual code merging. The steps for manual merging are as follows:
1. Based on the error message, open the generated code file (also with a `.go.gen<timestamp>` suffix format).
2. Locate the Go code block corresponding to `Foobar2` and delete this code block.
3. After manual adjustment, the Go file must meet the following requirements:
- The number of service code blocks in the Go file must match the number of services in the proto file, and their order must be consistent.
- The service code blocks in the Go file must be separated by a fixed marker: `// ---------- Do not delete or move this split line, this is the merge code marker ----------`.
- If the proto file contains only one service, the service code block in the Go file does not need a separator.
After manual adjustments are complete, the automatic merging feature will work correctly if the number of services in the proto file remains unchanged in the future.
<br>
### Warning
If multiple services are included in a proto file, once the code is generated, do not adjust the order of the services in the proto file, otherwise it will cause confusion in the automatic merging code, and manual adjustment of the code is required.

View File

@ -11,9 +11,13 @@ import (
"path"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"time"
"github.com/fatih/color"
"github.com/zhufuyi/sponge/pkg/gobash"
"github.com/zhufuyi/sponge/pkg/gofile"
)
@ -64,6 +68,7 @@ func (m *mergeParam) SetSplitLineMark(lineMark string) {
func (m *mergeParam) runMerge() {
files := gofile.FuzzyMatchFiles(m.dir + "/" + m.fuzzyFilename)
files = filterAndRemoveOldFiles(files)
for _, file := range files {
successFile, err := m.runMergeCode(file)
if err != nil {
@ -106,13 +111,10 @@ func (m *mergeParam) runMergeCode(file string) (string, error) {
count1 := bytes.Count(data1, m.splitLineMark)
count2 := bytes.Count(data2, m.splitLineMark)
//if count2 > count1 {
// // 判断新增加的service把新的service代码合并到原来的文件中
//} else if count2 < count1 {
//
//}
if count1 != count2 {
return "", fmt.Errorf("merge code mark mismatch, please merge codes manually, file = %s", file)
return "", fmt.Errorf(color.RedString("merge code failed (%s --> %s), manually merge code"+
" reference document https://github.com/zhufuyi/sponge/tree/main/cmd/sponge/commands/merge",
cutPathPrefix(file), getTargetFilename(file)))
}
var data []byte
@ -133,7 +135,9 @@ func (m *mergeParam) runMergeCode(file string) (string, error) {
}
if len(data1) > len(data) {
return "", fmt.Errorf("to avoid replacing logical code, please merge codes manually, file = %s", file)
return "", fmt.Errorf(color.RedString("merge code failed (%s --> %s), to avoid replacing logical code, "+
"manually merge code reference document https://github.com/zhufuyi/sponge/tree/main/cmd/sponge/commands/merge",
cutPathPrefix(file), getTargetFilename(file)))
}
if len(data1) == len(data) {
@ -175,8 +179,8 @@ func getOldFile(file string) string {
}
func compareCode(oldCode []code, newCode []code) ([]byte, []byte) {
var addCode []byte
var position []byte
var addCode []string
var position string
for _, code1 := range newCode {
isEqual := false
@ -187,14 +191,18 @@ func compareCode(oldCode []code, newCode []code) ([]byte, []byte) {
}
}
if !isEqual {
addCode = append(addCode, []byte(code1.value)...)
addCode = append(addCode, code1.value)
}
}
if len(oldCode) > 0 {
position = []byte(oldCode[len(oldCode)-1].value) // last position
l := len(oldCode)
if l > 0 {
position = oldCode[l-1].value // last position
}
return addCode, position
addData := checkAndAdjustErrorCode(addCode, position, l)
return addData, []byte(position)
}
func compareCode2(oldCode []code, newCode []code, data []byte) ([]byte, []byte) {
@ -239,8 +247,6 @@ func mergeCode(oldCode []byte, addCode []byte, position []byte) []byte {
if len(ss) != 2 {
return oldCode
}
fmt.Println("------ position = ", string(position))
fmt.Println("------ addCode = ", string(addCode))
data = append(ss[0], position...)
data = append(data, addCode...)
data = append(data, ss[1]...)
@ -366,6 +372,8 @@ func getComment(name string, str string) string {
return strings.ReplaceAll(match[0], "\nfunc", "")
}
// ------------------------------------------------------------------------------------------
func adaptDir(dir string) string {
if dir == "." || dir == "./" || dir == ".\\" {
return ""
@ -380,6 +388,124 @@ func adaptDir(dir string) string {
return dir + "/"
}
func cutPathPrefix(srcFile string) string {
dirPath, _ := filepath.Abs(".")
return strings.ReplaceAll(srcFile, dirPath+gofile.GetPathDelimiter(), "")
}
func getTargetFilename(file string) string {
filename := gofile.GetFilename(file)
ss := strings.Split(filename, ".go.gen")
if len(ss) != 2 {
return file
}
return ss[0] + ".go"
}
func filterAndRemoveOldFiles(files []string) []string {
if len(files) < 2 {
return files
}
var groupFiles = make(map[string][]string)
for _, file := range files {
filePrefix := strings.Split(file, ".go.gen")
if len(filePrefix) != 2 {
continue
}
if _, ok := groupFiles[filePrefix[0]]; !ok {
groupFiles[filePrefix[0]] = []string{file}
} else {
groupFiles[filePrefix[0]] = append(groupFiles[filePrefix[0]], file)
}
}
var newFiles, removeFiles []string
for _, fs := range groupFiles {
l := len(fs)
if l == 1 {
newFiles = append(newFiles, fs[0])
} else if l > 1 {
sort.Strings(fs)
newFiles = append(newFiles, fs[l-1])
removeFiles = append(removeFiles, fs[:l-1]...)
}
}
// remove old files
for _, file := range removeFiles {
_ = os.Remove(file)
}
return newFiles
}
var (
errCodeStrMark1 = "errcode.NewError("
errCodeStrMark2 = "errcode.NewRPCStatus("
)
func checkAndGetErrorCodeStr(str string) string {
if !strings.Contains(str, errCodeStrMark1) && !strings.Contains(str, errCodeStrMark2) {
return ""
}
// match strings between left parentheses and commas using regular expressions
// string format: ErrLoginUser = errcode.NewError(userBaseCode+2, "failed to Login "+userName)
pattern := `\(([^)]+?),`
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(str)
if len(match) < 2 {
return ""
}
return match[1]
}
func parseErrorCode(str string) (string, int) {
ss := strings.Split(str, "+")
if len(ss) != 2 {
return "", 0
}
num, _ := strconv.Atoi(strings.TrimSpace(ss[1]))
return ss[0], num
}
func checkAndAdjustErrorCode(addCode []string, position string, l int) []byte {
data := []byte(strings.Join(addCode, ""))
str := checkAndGetErrorCodeStr(position)
if str == "" {
return data
}
referenceStr, maxNum := parseErrorCode(str)
if referenceStr == "" || maxNum == 0 {
return data
}
if maxNum < l {
maxNum = l
}
// adjust error code
var newCode []byte
for _, line := range addCode {
codeStr := checkAndGetErrorCodeStr(line)
if codeStr == "" {
return data
}
baseStr, num := parseErrorCode(codeStr)
if baseStr == "" || num == 0 {
return data
}
maxNum++
newLine := strings.ReplaceAll(line, codeStr, fmt.Sprintf("%s+%d", referenceStr, maxNum))
newCode = append(newCode, []byte(newLine)...)
}
return newCode
}
// ------------------------------------------------------------------------------------------
func mergeHTTPECode(dir string) {

View File

@ -1,6 +1,7 @@
package merge
import (
"github.com/fatih/color"
"github.com/spf13/cobra"
)
@ -11,11 +12,15 @@ func GinHandlerCode() *cobra.Command {
cmd := &cobra.Command{
Use: "http-pb",
Short: "Merge the generated http related code into the template file",
Long: `merge the generated http related code into the template file.
Long: color.HiBlackString(`merge the generated http related code into the template file.
Examples:
# merge go template file in local server directory
sponge merge http-pb
`,
# merge go template file in specified directory
sponge merge http-pb --dir=/path/to/server/directory
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -0,0 +1,123 @@
## 合并命令
合并命令用于将生成的代码自动合并到已有的模板文件中,无需担心影响已编写的业务逻辑代码。如果合并过程中出现意外,合并前的代码备份会保存在 `/tmp/sponge_merge_backup_code` 目录中,您可以从中恢复之前的代码状态。
当自动合并代码出错时(proto文件中service数量变化导致),需要手动合并代码。
<br>
### 手动合并代码说明
在大多数情况下,一个 proto 文件通常定义一个 service。但在开发过程中proto 文件中的 service 数量可能会有所变化,例如从一个 service 增加到多个,或者从多个 service 减少为一个。这种变化可能导致自动合并代码失败,此时就需要手动进行代码合并。
#### 增加 Service 时的手动合并
以下以 `greeter.proto` 为例,初始文件中只有一个 service名为 `Foobar1`。文件内容如下:
```protobuf
syntax = "proto3";
package greeter;
option go_package = "greeter";
service Foobar1 {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
```
在只有一个 service 的情况下,自动合并代码通常能正常工作,无需手动干预。
假设现在需要在 `greeter.proto` 中增加一个名为 `Foobar2` 的 service更新后的文件内容如下
```protobuf
syntax = "proto3";
package greeter;
option go_package = "greeter";
service Foobar1 {
rpc SayHello (SayHelloRequest) returns (SayHelloReply) {}
}
message SayHelloRequest {
string name = 1;
}
message SayHelloReply {
string message = 1;
}
service Foobar2 {
rpc SayHello (SayHelloRequest) returns (SayHelloReply) {}
}
```
当 proto 文件中新增 service 后,自动合并代码会导致失败,此时需要手动合并代码。手动合并的步骤如下:
1. 根据错误提示,找到生成的代码文件(文件后缀格式为 `.go.gen<日期时间>`),打开文件。
2. 找到 service `Foobar2` 对应的 Go 代码块,将其复制到目标 Go 文件中(即与 `.go.gen<日期时间>`文件相同的前缀go文件
3. 复制后的 Go 文件需符合以下要求:
- Go文件的 service 代码块数量与 proto 文件的 service 一样,并且顺序必须一致。
- Go文件的 service 代码块必须有固定的分割标记:`// ---------- Do not delete or move this split line, this is the merge code marker ----------`。
- 当 proto 文件中仅剩一个 service 时Go文件的 service 代码块不需要分割标记。
通过手动合并代码,后续如果 proto 文件中 service 数量不变化,自动合并功能都可以正常工作。
<br>
#### 减少 Service 时的手动合并
继续以 `greeter.proto` 为例,此时文件中包含两个 service`Foobar1` 和 `Foobar2`。在这种情况下,自动合并代码通常能正常运行,无需手动干预。
假设现在需要删除 `Foobar2`,更新后的 `greeter.proto` 文件内容如下:
```protobuf
syntax = "proto3";
package greeter;
option go_package = "greeter";
service Foobar1 {
rpc SayHello (SayHelloRequest) returns (SayHelloReply) {}
}
message SayHelloRequest {
string name = 1;
}
message SayHelloReply {
string message = 1;
}
```
此时,自动合并代码会导致失败,需手动合并代码。手动合并的步骤如下:
1. 根据错误提示,打开生成的代码文件(同样为 `.go.gen<时间戳>` 格式)。
2. 找到与 `Foobar2` 对应的 Go 代码块,删除该代码块。
3. 手动调整后go文件需满足以下要求
- Go文件的 service 代码块数量与 proto 文件的 service 一样,并且顺序必须一致。
- Go文件的 service 代码块必须有固定的分割标记:`// ---------- Do not delete or move this split line, this is the merge code marker ----------`。
- 当 proto 文件中仅剩一个 service 时Go文件的 service 代码块不需要分割标记。
手动调整完成后,后续如果 proto 文件中 service 数量不变化,自动合并功能都可以正常工作。
<br>
### 警告
如果在一个 proto 文件中包含多个service, 一旦生成代码之后不要调整proto文件中的service的顺序否则会导致自动合并代码混乱此时只能手动调整合并后的代码。

View File

@ -1,6 +1,7 @@
package merge
import (
"github.com/fatih/color"
"github.com/spf13/cobra"
)
@ -11,12 +12,15 @@ func GinServiceCode() *cobra.Command {
cmd := &cobra.Command{
Use: "rpc-gw-pb",
Short: "Merge the generated grpc gateway related code into the template file",
Long: `merge the generated grpc gateway related code into the template file.
Long: color.HiBlackString(`merge the generated grpc gateway related code into the template file.
Examples:
# merge go template file in local server directory
sponge merge rpc-gw-pb
sponge merge rpc-gw-pb --dir=yourServerDir
`,
# merge go template file in specified server directory
sponge merge rpc-gw-pb --dir=/path/to/server/directory
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -1,6 +1,7 @@
package merge
import (
"github.com/fatih/color"
"github.com/spf13/cobra"
)
@ -11,11 +12,15 @@ func GRPCServiceCode() *cobra.Command {
cmd := &cobra.Command{
Use: "rpc-pb",
Short: "Merge the generated grpc related code into the template file",
Long: `merge the generated grpc related code into the template file.
Long: color.HiBlackString(`merge the generated grpc related code into the template file.
Examples:
# merge go template file in local server directory
sponge merge rpc-pb
`,
# merge go template file in specified directory
sponge merge rpc-pb --dir=/path/to/server/directory
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -22,10 +22,11 @@ func PatchCommand() *cobra.Command {
patch.GenMysqlInitCommand(),
patch.GenTypesPbCommand(),
patch.CopyProtoCommand(),
patch.CopyThirdPartyProtoCommand(),
patch.CopyGOModCommand(),
patch.ModifyDuplicateNumCommand(),
patch.ModifyDuplicateErrCodeCommand(),
patch.AdaptMonoRepoCommand(),
patch.AddSpecialTypesCommand(),
patch.ModifyProtoPackageCommand(),
)

View File

@ -6,6 +6,7 @@ import (
"fmt"
"os"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/pkg/gofile"
@ -22,13 +23,15 @@ func AdaptMonoRepoCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "adapt-mono-repo",
Short: "Adapt to mono-repo in api directory code",
Long: `adapt to mono-repo in api directory code
Long: color.HiBlackString(`adapt to mono-repo in api directory code
Examples:
# adapt to mono-repo code
# adapt to mono-repo code in local server directory
sponge patch adapt-mono-repo
`,
# adapt to mono-repo code in specified directory
sponge patch adapt-mono-repo --dir=/path/to/server/directory
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
@ -49,8 +52,8 @@ Examples:
return err
}
var oldStr = fmt.Sprintf("\"%s/api", moduleName)
var newStr = fmt.Sprintf("\"%s/api", moduleName+"/"+serverName)
var oldStr = fmt.Sprintf("\"%s/api", moduleName+"/"+serverName)
var newStr = fmt.Sprintf("\"%s/api", moduleName)
for _, file := range files {
data, err := os.ReadFile(file)
if err != nil {

View File

@ -1,9 +1,12 @@
package patch
import (
"errors"
"os"
"path/filepath"
"strings"
"github.com/zhufuyi/sponge/pkg/gofile"
)
// get moduleName and serverName from directory
@ -37,3 +40,26 @@ func cutPathPrefix(srcProtoFile string) string {
srcProtoFile = strings.ReplaceAll(srcProtoFile, dirPath, ".")
return strings.ReplaceAll(srcProtoFile, "\\", "/")
}
func listErrCodeFiles(dir string) ([]string, error) {
files, err := gofile.ListFiles(dir)
if err != nil {
return nil, err
}
if len(files) == 0 {
return nil, errors.New("not found files")
}
filterFiles := []string{}
for _, file := range files {
if strings.Contains(file, "systemCode_http.go") || strings.Contains(file, "systemCode_rpc.go") {
continue
}
if strings.Contains(file, "_http.go") || strings.Contains(file, "_rpc.go") {
filterFiles = append(filterFiles, file)
}
}
return filterFiles, nil
}

View File

@ -0,0 +1,86 @@
package patch
import (
"errors"
"fmt"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
"github.com/zhufuyi/sponge/pkg/gofile"
)
// CopyGOModCommand copy go mod files
func CopyGOModCommand() *cobra.Command {
var (
moduleName string // module name for go.mod
outPath string // output directory
isLogExist bool
)
cmd := &cobra.Command{
Use: "copy-go-mod",
Short: "Copy go mod files",
Long: color.HiBlackString(`copy go mod files to local directory.
Examples:
# copy go mod files to current directory
sponge patch copy-go-mod --module-name=yourModuleName
# copy go mod files to yourServerDir, module name from out directory
sponge patch copy-go-mod --out=./yourServerDir
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
if moduleName == "" {
mn, _, _ := getNamesFromOutDir(outPath)
if mn == "" {
return errors.New("module-name is required, please use --module-name to set it")
}
moduleName = mn
}
goModFile := outPath + gofile.GetPathDelimiter() + "go.mod"
if gofile.IsExists(goModFile) {
if isLogExist {
fmt.Printf("%s already exists, skip copying.\n", goModFile)
}
return nil
}
out, err := runCopyGoModCommand(moduleName, outPath)
if err != nil {
return err
}
fmt.Printf("copied go.mod to %s\n", out)
return nil
},
}
cmd.Flags().StringVarP(&moduleName, "module-name", "m", "", "module-name is the name of the module in the go.mod file")
cmd.Flags().StringVarP(&outPath, "out", "o", ".", "output directory")
cmd.Flags().BoolVarP(&isLogExist, "is-log-exist", "l", false, "is log file exist")
return cmd
}
func runCopyGoModCommand(moduleName string, out string) (string, error) {
r := generate.Replacers[generate.TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
}
// setting up template information
subFiles := []string{"sponge/go.mod", "sponge/go.sum"}
r.SetSubDirsAndFiles(nil, subFiles...)
r.SetReplacementFields(generate.GetGoModFields(moduleName))
_ = r.SetOutputDir(out)
if err := r.SaveFiles(); err != nil {
return "", err
}
return r.GetOutputDir(), nil
}

View File

@ -11,6 +11,7 @@ import (
"strings"
"time"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/pkg/gobash"
@ -32,7 +33,7 @@ func CopyProtoCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "copy-proto",
Short: "Copy proto file from the grpc service directory",
Long: `copy proto file from the grpc service, if the proto file exists, it will be forced to overwrite it,
Long: color.HiBlackString(`copy proto file from the grpc service, 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.
@ -45,7 +46,7 @@ Examples:
# copy the specified proto files in the grpc service directory
sponge patch copy-proto --server-dir=../grpc-server --proto-file=name1.proto,name2.proto
`,
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -0,0 +1,77 @@
package patch
import (
"errors"
"fmt"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
"github.com/zhufuyi/sponge/pkg/gofile"
)
// CopyThirdPartyProtoCommand copy third-party proto files
func CopyThirdPartyProtoCommand() *cobra.Command {
var (
outPath string // output directory
isLogExist bool
)
cmd := &cobra.Command{
Use: "copy-third-party-proto",
Short: "Copy third-party proto files",
Long: color.HiBlackString(`copy third-party proto files to local directory.
Examples:
# copy third-party proto files to current directory
sponge patch copy-third-party-proto
# copy third-party proto files to yourServerDir
sponge patch copy-third-party-proto --out=./yourServerDir
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
out := outPath + gofile.GetPathDelimiter() + "third_party"
if gofile.IsExists(out) {
if isLogExist {
fmt.Printf("%s proto files already exists, skip copying.\n", out)
}
return nil
}
var err error
out, err = runCopyThirdPartyProtoCommand(outPath)
if err != nil {
return err
}
fmt.Printf("copied third_party proto files to %s\n", out)
return nil
},
}
cmd.Flags().StringVarP(&outPath, "out", "o", ".", "output directory")
cmd.Flags().BoolVarP(&isLogExist, "is-log-exist", "l", false, "is log file exist")
return cmd
}
func runCopyThirdPartyProtoCommand(out string) (string, error) {
r := generate.Replacers[generate.TplNameSponge]
if r == nil {
return "", errors.New("replacer is nil")
}
// setting up template information
subDirs := []string{"sponge/third_party"}
r.SetSubDirsAndFiles(subDirs)
_ = r.SetOutputDir(out)
if err := r.SaveFiles(); err != nil {
return "", err
}
return r.GetOutputDir(), nil
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/pkg/gofile"
@ -20,7 +21,7 @@ func DeleteJSONOmitemptyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "del-omitempty",
Short: "Delete json tag omitempty",
Long: `delete json tag omitempty
Long: color.HiBlackString(`delete json tag omitempty
Examples:
# delete all files that include the omitempty character
@ -29,7 +30,7 @@ Examples:
# delete the specified suffix file including the omitempty character
sponge patch del-omitempty --dir=./api --suffix-name=pb.go
`,
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
@ -24,7 +25,7 @@ func GenerateDBInitCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "gen-db-init",
Short: "Generate database initialization code",
Long: `generate database initialization code.
Long: color.HiBlackString(`generate database initialization code.
Examples:
# generate mysql initialization code.
@ -32,7 +33,7 @@ Examples:
# 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-db-init --db-driver=mysql --out=./yourServerDir
`,
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
@ -46,7 +47,6 @@ Examples:
if serverName == "" {
return fmt.Errorf(`serverName is empty`)
}
targetFile = strings.ReplaceAll(targetFile, "internal", "Internal")
}
var isEmpty bool
@ -171,7 +171,7 @@ func (g *dbInitGenerator) addFields(r replacer.Replacer) []replacer.Field {
}...)
if g.suitedMonoRepo {
fs := generate.SubServerCodeFields(r.GetOutputDir(), g.moduleName, g.serverName)
fs := generate.SubServerCodeFields(g.moduleName, g.serverName)
fields = append(fields, fs...)
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
@ -23,7 +24,7 @@ func GenMysqlInitCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "gen-mysql-init",
Short: "Generate mysql initialization code",
Long: `generate mysql initialization code
Long: color.HiBlackString(`generate mysql initialization code
Examples:
# generate mysql initialization code.
@ -31,7 +32,7 @@ Examples:
# 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,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
@ -23,7 +24,7 @@ func GenTypesPbCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "gen-types-pb",
Short: "Generate types.proto code",
Long: `generate types.proto code
Long: color.HiBlackString(`generate types.proto code
Examples:
# generate types.proto code.
@ -31,7 +32,7 @@ Examples:
# 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,
RunE: func(cmd *cobra.Command, args []string) error {

View File

@ -1,6 +1,7 @@
package patch
import (
"bufio"
"bytes"
"fmt"
"os"
@ -8,6 +9,7 @@ import (
"strconv"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
)
@ -20,13 +22,12 @@ func ModifyDuplicateErrCodeCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "modify-dup-err-code",
Short: "Modify duplicate error codes",
Long: `modify duplicate error codes
Long: color.HiBlackString(`modify duplicate error codes
Examples:
# modify duplicate error codes
sponge patch modify-dup-err-code --dir=internal/ecode
`,
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
@ -35,27 +36,16 @@ Examples:
return err
}
var total int
for _, file := range files {
ecsis, err := parseErrCodeInfo(file)
count, err := checkAndModifyDuplicateErrCode(file)
if err != nil {
return err
}
for _, ecsi := range ecsis {
msg, err := ecsi.modifyHTTPDuplicateNum()
if err != nil {
return err
}
if msg != "" {
fmt.Println("modify http duplicate error codes: ", msg)
}
msg, err = ecsi.modifyGRPCDuplicateNum()
if err != nil {
return err
}
if msg != "" {
fmt.Println("modify grpc duplicate error codes: ", msg)
}
}
total += count
}
if total > 0 {
fmt.Println("modify duplicate error codes successfully.")
}
return nil
},
@ -67,228 +57,121 @@ Examples:
}
type eCodeInfo struct {
Name string
Num int
Str string
Name string
Num int
Str string
DstStr string
}
type errCodesInfo struct {
file string
var (
serviceGroupSeparatorMark = "// ---------- Do not delete or move this split line, this is the merge code marker ----------"
defineHTTPErrCodeMark = "errcode.NewError("
defineGRPCErrCodeMark = "errcode.NewRPCStatus("
)
httpErrCodeInfo map[string]eCodeInfo // map[name]eCodeInfo
httpDuplicationNums map[int][]string
httpMaxNum int
func parseErrCodeInfo(line string) eCodeInfo {
ci := eCodeInfo{}
grpcErrCodeInfo map[string]eCodeInfo // map[name]eCodeInfo
grpcDuplicationNums map[int][]string
grpcMaxNum int
pattern := `(\w+)\s*=\s*\w+\.(.*?)\((.*?),`
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(line)
if len(match) < 4 {
return ci
}
baseCodeStr := match[3]
index := strings.Index(line, baseCodeStr)
if index < 0 {
return ci
}
srcStr := line[:index] + baseCodeStr
ss := strings.Split(baseCodeStr, "+")
if len(ss) != 2 {
return ci
}
num, _ := strconv.Atoi(strings.TrimSpace(ss[1]))
ci.Name = match[1]
ci.Num = num
ci.Str = srcStr
ci.DstStr = line[:index] + ss[0] + "+"
return ci
}
func (e *errCodesInfo) getHTTPMaxNum() int {
maxNum := 0
for num := range e.httpDuplicationNums {
if num > maxNum {
maxNum = num
func getModifyCodeInfos(codes []eCodeInfo) ([]eCodeInfo, int) {
maxCode := 0
m := map[int][]eCodeInfo{}
for _, ci := range codes {
if ci.Num > maxCode {
maxCode = ci.Num
}
if cis, ok := m[ci.Num]; ok {
m[ci.Num] = append(cis, ci)
} else {
m[ci.Num] = []eCodeInfo{ci}
}
}
return maxNum
}
func (e *errCodesInfo) getGRPCMaxNum() int {
maxNum := 0
for num := range e.grpcDuplicationNums {
if num > maxNum {
maxNum = num
needModify := []eCodeInfo{}
for _, infos := range m {
if len(infos) > 1 {
needModify = append(needModify, infos[1:]...)
}
}
return maxNum
return needModify, maxCode
}
func (e *errCodesInfo) modifyHTTPDuplicateNum() (string, error) {
msg := ""
duplicateNums := []string{}
if len(e.httpDuplicationNums) == 0 {
return msg, nil
func modifyErrCode(data []byte, infos []eCodeInfo, maxCode int) []byte {
for _, info := range infos {
maxCode++
data = bytes.ReplaceAll(data, []byte(info.Str), []byte(info.DstStr+strconv.Itoa(maxCode)))
}
return data
}
numMap := map[int]struct{}{}
for num := range e.httpDuplicationNums {
numMap[num] = struct{}{}
}
func getDuplicateErrCodeInfo(data []byte) ([]eCodeInfo, int) {
cis := []eCodeInfo{}
e.httpMaxNum = e.getHTTPMaxNum()
for _, names := range e.httpDuplicationNums {
if len(names) <= 1 {
buf := bufio.NewReader(bytes.NewReader(data))
for {
line, err := buf.ReadString('\n')
if err != nil {
break
}
if !strings.Contains(line, defineHTTPErrCodeMark) && !strings.Contains(line, defineGRPCErrCodeMark) {
continue
}
for i, name := range names {
if i == 0 {
continue
}
eci := e.httpErrCodeInfo[name]
e.httpMaxNum++
newNum := e.httpMaxNum
_, err := updateErrCodeFile(e.file, newNum, eci)
if err != nil {
return msg, err
}
duplicateNums = append(duplicateNums, fmt.Sprintf("%d --> %d", eci.Num, newNum))
ci := parseErrCodeInfo(line)
if ci.Name != "" {
cis = append(cis, ci)
}
}
if len(duplicateNums) == 0 {
return msg, nil
}
return strings.Join(duplicateNums, ", "), nil
return getModifyCodeInfos(cis)
}
func (e *errCodesInfo) modifyGRPCDuplicateNum() (string, error) {
msg := ""
duplicateNums := []string{}
if len(e.grpcDuplicationNums) == 0 {
return msg, nil
}
numMap := map[int]struct{}{}
for num := range e.grpcDuplicationNums {
numMap[num] = struct{}{}
}
e.grpcMaxNum = e.getGRPCMaxNum()
for _, names := range e.grpcDuplicationNums {
if len(names) <= 1 {
continue
}
for i, name := range names {
if i == 0 {
continue
}
eci := e.grpcErrCodeInfo[name]
e.grpcMaxNum++
newNum := e.grpcMaxNum
_, err := updateErrCodeFile(e.file, newNum, eci)
if err != nil {
return msg, err
}
duplicateNums = append(duplicateNums, fmt.Sprintf("%d --> %d", eci.Num, newNum))
}
}
if len(duplicateNums) == 0 {
return msg, nil
}
return strings.Join(duplicateNums, ", "), nil
}
func parseErrCodeInfo(file string) ([]*errCodesInfo, error) {
errCodeType := ""
ecsis := []*errCodesInfo{}
func checkAndModifyDuplicateErrCode(file string) (int, error) {
data, err := os.ReadFile(file)
if err != nil {
return ecsis, err
}
dataStr := string(data)
if strings.Contains(dataStr, "errcode.NewError") {
errCodeType = httpType
} else if strings.Contains(dataStr, "errcode.NewRPCStatus") {
errCodeType = grpcType
return 0, err
}
if errCodeType == "" {
return ecsis, nil
serviceGroupData := bytes.Split(data, []byte(serviceGroupSeparatorMark))
var fileContent [][]byte
var count int
for _, groupData := range serviceGroupData {
ecis, maxCode := getDuplicateErrCodeInfo(groupData)
fileContent = append(fileContent, modifyErrCode(groupData, ecis, maxCode))
count += len(ecis)
}
var regStr string
if errCodeType == httpType {
regStr = `(Err[\w\W]*?)[ ]*?=[ ]*?errcode.NewError\(([\w\W]*?)BaseCode\+(\d),`
} else if errCodeType == grpcType {
regStr = `(Status[\w\W]*?)[ ]*?=[ ]*?errcode.NewRPCStatus\(([\w\W]*?)BaseCode\+(\d),`
}
reg := regexp.MustCompile(regStr)
allSubMatch := reg.FindAllStringSubmatch(dataStr, -1)
if len(allSubMatch) == 0 {
return ecsis, nil
}
groupNames := make(map[string][][]string)
for _, match := range allSubMatch {
if len(match) == 4 {
gns, ok := groupNames[match[2]]
if ok {
gns = append(gns, match)
} else {
gns = [][]string{match}
}
groupNames[match[2]] = gns
}
continue
}
for _, gn := range groupNames {
ecsi := &errCodesInfo{}
eci := make(map[string]eCodeInfo)
duplicationNums := make(map[int][]string)
for _, match := range gn {
if len(match) == 4 {
num, _ := strconv.Atoi(match[3])
if num == 0 {
continue
}
if names, ok := duplicationNums[num]; ok {
duplicationNums[num] = append(names, match[1])
} else {
duplicationNums[num] = []string{match[1]}
}
eci[match[1]] = eCodeInfo{Name: match[1], Num: num, Str: match[0]}
}
}
if errCodeType == httpType {
ecsi.httpDuplicationNums = duplicationNums
ecsi.httpErrCodeInfo = eci
} else if errCodeType == grpcType {
ecsi.grpcDuplicationNums = duplicationNums
ecsi.grpcErrCodeInfo = eci
}
ecsi.file = file
ecsis = append(ecsis, ecsi)
}
return ecsis, nil
}
func updateErrCodeFile(file string, newNum int, eci eCodeInfo) (eCodeInfo, error) {
strTmp := eci.Str
oldNum := eci.Num
eci.Str = replaceNumStr(strTmp, oldNum, newNum)
eci.Num = newNum
data, err := os.ReadFile(file)
if err != nil {
return eci, err
}
data = bytes.ReplaceAll(data, []byte(strTmp), []byte(eci.Str))
err = os.WriteFile(file, data, 0766)
if err != nil {
return eci, err
}
return eci, nil
}
func replaceNumStr(str string, oldNum int, newNum int) string {
oldNumStr := fmt.Sprintf("+%d", oldNum)
newNumStr := fmt.Sprintf("+%d", newNum)
return strings.ReplaceAll(str, oldNumStr, newNumStr)
data = bytes.Join(fileContent, []byte(serviceGroupSeparatorMark))
err = os.WriteFile(file, data, 0666)
return count, err
}

View File

@ -2,23 +2,14 @@ package patch
import (
"bytes"
"errors"
"fmt"
"os"
"regexp"
"sort"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/pkg/gofile"
"github.com/zhufuyi/sponge/pkg/krand"
)
const (
httpType = "http"
grpcType = "grpc"
)
// ModifyDuplicateNumCommand modify duplicate numbers
@ -30,13 +21,12 @@ func ModifyDuplicateNumCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "modify-dup-num",
Short: "Modify duplicate numbers",
Long: `modify duplicate numbers
Long: color.HiBlackString(`modify duplicate numbers
Examples:
# modify duplicate numbers
sponge patch modify-dup-num --dir=internal/ecode
`,
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
@ -45,25 +35,14 @@ Examples:
return err
}
nsi, err := parseFiles(files)
count, err := checkAndModifyDuplicateNum(files)
if err != nil {
return err
}
if count > 0 {
fmt.Println("modify duplicate num successfully.")
}
msg, err := nsi.modifyHTTPDuplicateNum()
if err != nil {
return err
}
if msg != "" {
fmt.Println("modify http duplicate numbers: ", msg)
}
msg, err = nsi.modifyGRPCDuplicateNum()
if err != nil {
return err
}
if msg != "" {
fmt.Println("modify grpc duplicate numbers: ", msg)
}
return nil
},
}
@ -73,315 +52,161 @@ Examples:
return cmd
}
func listErrCodeFiles(dir string) ([]string, error) {
files, err := gofile.ListFiles(dir)
type coreInfo struct {
name string
num int
srcStr string
dstStr string
file string
}
var (
httpNumMark = "errcode.HCode"
grpcNumMark = "errcode.RCode"
httpPattern = `errcode\.HCode\(([^)]+)\)`
grpcPattern = `errcode\.RCode\(([^)]+)\)`
)
func getVariableName(data []byte, pattern string) string {
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(string(data))
if len(match) < 2 {
return ""
}
return strings.ReplaceAll(match[1], " ", "")
}
func parseNumInfo(data []byte, variableName string) coreInfo {
var info coreInfo
pattern := variableName + `\s*=\s*(\d+)`
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(string(data))
if len(match) < 2 {
return info
}
num, err := strconv.Atoi(match[1])
if err != nil {
return nil, err
return info
}
if len(files) == 0 {
return nil, errors.New("not found files")
ss := strings.Split(match[0], "=")
if len(ss) != 2 {
return info
}
filterFiles := []string{}
for _, file := range files {
if strings.Contains(file, "systemCode.go") ||
strings.Contains(file, "systemCode_http.go") ||
strings.Contains(file, "systemCode_rpc.go") {
continue
}
filterFiles = append(filterFiles, file)
}
info.name = variableName
info.num = num
info.srcStr = match[0]
info.dstStr = ss[0] + "= "
return filterFiles, nil
return info
}
type numInfo struct {
Name string
Num int
Str string
}
type numbersInfo struct {
httpNumInfo map[string]map[string]numInfo // map[file]map[code]numInfo
httpDuplicationNums map[int][]string
grpcNumInfo map[string]map[string]numInfo // map[file]map[code]numInfo
grpcDuplicationNums map[int][]string
}
func (r *numbersInfo) modifyHTTPDuplicateNum() (string, error) {
msg := ""
duplicateNums := []string{}
if len(r.httpDuplicationNums) == 0 {
return msg, nil
}
numMap := map[int]struct{}{}
for num := range r.httpDuplicationNums {
numMap[num] = struct{}{}
}
for num, fs := range r.httpDuplicationNums {
if len(fs) <= 1 {
continue
}
fs = sortFiles(fs)
for i, file := range fs {
if i == 0 {
continue
}
for _, ni := range r.httpNumInfo[file] {
newNum := genNewNum(numMap)
if ni.Num == num {
_, err := updateFile(file, newNum, ni)
if err != nil {
return msg, err
}
duplicateNums = append(duplicateNums, fmt.Sprintf("%d --> %d", ni.Num, newNum))
}
}
}
}
if len(duplicateNums) == 0 {
return msg, nil
}
return strings.Join(duplicateNums, ", "), nil
}
func (r *numbersInfo) modifyGRPCDuplicateNum() (string, error) {
msg := ""
duplicateNums := []string{}
if len(r.grpcDuplicationNums) == 0 {
return msg, nil
}
numMap := map[int]struct{}{}
for num := range r.grpcDuplicationNums {
numMap[num] = struct{}{}
}
for num, fs := range r.grpcDuplicationNums {
if len(fs) <= 1 {
continue
}
fs = sortFiles(fs)
for i, file := range fs {
if i == 0 {
continue
}
for _, ni := range r.grpcNumInfo[file] {
newNum := genNewNum(numMap)
if ni.Num == num {
_, err := updateFile(file, newNum, ni)
if err != nil {
return msg, err
}
duplicateNums = append(duplicateNums, fmt.Sprintf("%d --> %d", ni.Num, newNum))
}
}
}
}
if len(duplicateNums) == 0 {
return msg, nil
}
return strings.Join(duplicateNums, ", "), nil
}
func parseFiles(files []string) (*numbersInfo, error) {
nsi := &numbersInfo{
httpNumInfo: map[string]map[string]numInfo{},
httpDuplicationNums: map[int][]string{},
grpcNumInfo: map[string]map[string]numInfo{},
grpcDuplicationNums: map[int][]string{},
}
for _, file := range files {
result, err := parseNumberInfo(file)
if err != nil {
return nsi, err
}
if result == nil {
continue
}
if result.errCodeType == httpType {
for _, num := range result.nums {
if fs, ok := nsi.httpDuplicationNums[num]; ok {
fs = append(fs, file)
nsi.httpDuplicationNums[num] = fs
} else {
nsi.httpDuplicationNums[num] = []string{file}
}
}
nsi.httpNumInfo[file] = result.ni
} else if result.errCodeType == grpcType {
for _, num := range result.nums {
if fs, ok := nsi.grpcDuplicationNums[num]; ok {
fs = append(fs, file)
nsi.grpcDuplicationNums[num] = fs
} else {
nsi.grpcDuplicationNums[num] = []string{file}
}
}
nsi.grpcNumInfo[file] = result.ni
}
}
return nsi, nil
}
type parseResult struct {
ni map[string]numInfo
nums []int
errCodeType string
}
func parseNumberInfo(file string) (*parseResult, error) {
errCodeType := ""
ni := map[string]numInfo{}
nums := []int{}
func getNumberInfos(file string) []coreInfo {
var infos []coreInfo
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
dataStr := string(data)
if strings.Contains(dataStr, "errcode.NewError") {
errCodeType = httpType
} else if strings.Contains(dataStr, "errcode.NewRPCStatus") {
errCodeType = grpcType
return infos
}
if errCodeType == "" {
return nil, nil
}
var regStr string
if errCodeType == httpType {
regStr = `=[ ]*?errcode.HCode\(([\w\W]*?)\)\n`
} else if errCodeType == grpcType {
regStr = `=[ ]*?errcode.RCode\(([\w\W]*?)\)\n`
}
reg := regexp.MustCompile(regStr)
allSubMatch := reg.FindAllStringSubmatch(dataStr, -1)
if len(allSubMatch) == 0 {
return nil, nil
}
names := []string{}
for _, match := range allSubMatch {
for i, v := range match {
if i == 1 {
names = append(names, v)
serviceGroupData := bytes.Split(data, []byte(serviceGroupSeparatorMark))
for _, groupData := range serviceGroupData {
pattern := ""
if bytes.Contains(groupData, []byte(httpNumMark)) {
pattern = httpPattern
} else if bytes.Contains(groupData, []byte(grpcNumMark)) {
pattern = grpcPattern
}
if pattern != "" {
variableName := getVariableName(groupData, pattern)
if variableName != "" {
info := parseNumInfo(groupData, variableName)
if info.name != "" {
info.file = file
infos = append(infos, info)
}
}
}
}
for _, name := range names {
regStr = name + `[ ]*?=[ ]*?([\d]+)`
reg = regexp.MustCompile(regStr)
allSubMatch = reg.FindAllStringSubmatch(dataStr, -1)
for _, match := range allSubMatch {
if len(match) == 2 {
num, _ := strconv.Atoi(match[1])
nums = append(nums, num)
ni[name] = numInfo{Name: name, Num: num, Str: match[0]}
return infos
}
func getModifyNumInfos(infos []coreInfo) ([]coreInfo, map[int]struct{}) {
m := map[int][]coreInfo{}
allNum := map[int]struct{}{}
for _, info := range infos {
allNum[info.num] = struct{}{}
if cis, ok := m[info.num]; ok {
m[info.num] = append(cis, info)
} else {
m[info.num] = []coreInfo{info}
}
}
needModify := []coreInfo{}
for _, numInfos := range m {
if len(numInfos) > 1 {
needModify = append(needModify, numInfos[1:]...)
}
}
return needModify, allNum
}
func modifyNumberInfos(infos []coreInfo, allNum map[int]struct{}) (int, error) {
l := len(infos)
if l == 0 {
return 0, nil
}
var nums []int
for i := 1; i < 100; i++ {
if _, ok := allNum[i]; !ok {
nums = append(nums, i)
if len(nums) == len(infos) {
break
}
}
}
return &parseResult{
ni: ni,
nums: nums,
errCodeType: errCodeType,
}, nil
}
func getFileCreateTime(file string) int64 {
fi, err := os.Stat(file)
if err != nil {
return 0
}
return fi.ModTime().Unix()
}
func updateFile(file string, newNum int, ni numInfo) (numInfo, error) {
strTmp := ni.Str
ni.Num = newNum
ni.Str = replaceNum(strTmp, ni.Num)
data, err := os.ReadFile(file)
if err != nil {
return ni, err
}
data = bytes.ReplaceAll(data, []byte(strTmp), []byte(ni.Str))
err = os.WriteFile(file, data, 0766)
if err != nil {
return ni, err
}
return ni, nil
}
func replaceNum(str string, newNum int) string {
regStr := `([\w\W]*?=[ ]*?)[\d]+`
reg := regexp.MustCompile(regStr)
allSubMatch := reg.FindAllStringSubmatch(str, -1)
for _, match := range allSubMatch {
if len(match) == 2 {
str = match[1] + fmt.Sprintf("%d", newNum)
if len(nums) < l {
for i := 0; i < l-len(nums); i++ {
nums = append(nums, 99) // 99 is the largest number
}
}
return str
}
type fileInfo struct {
file string
createdTime int64
}
func sortFiles(files []string) []string {
fis := []*fileInfo{}
for _, file := range files {
fis = append(fis, &fileInfo{
file: file,
createdTime: getFileCreateTime(file),
})
}
sort.Slice(fis, func(i, j int) bool {
return fis[i].createdTime < fis[j].createdTime
})
var sFiles []string
for _, fi := range fis {
sFiles = append(sFiles, fi.file)
}
return sFiles
}
func genNewNum(numMap map[int]struct{}) int {
max := 1000000
count := 0
for {
count++
newNum := krand.Int(99)
if _, ok := numMap[newNum]; !ok {
numMap[newNum] = struct{}{}
return newNum
for i, info := range infos {
data, err := os.ReadFile(info.file)
if err != nil {
return 0, err
}
if count > max {
break
newData := bytes.ReplaceAll(data, []byte(info.srcStr), []byte(info.dstStr+strconv.Itoa(nums[i])))
err = os.WriteFile(info.file, newData, 0666)
if err != nil {
return 0, err
}
count++
}
return count, nil
}
func checkAndModifyDuplicateNum(files []string) (int, error) {
var allInfos []coreInfo
for _, file := range files {
infos := getNumberInfos(file)
if len(infos) > 0 {
allInfos = append(allInfos, infos...)
}
}
return 1
needModify, allNum := getModifyNumInfos(allInfos)
return modifyNumberInfos(needModify, allNum)
}

View File

@ -8,6 +8,7 @@ import (
"regexp"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/zhufuyi/sponge/pkg/gofile"
@ -24,7 +25,7 @@ func ModifyProtoPackageCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "modify-proto-package",
Short: "Modifies the package and go_package names of proto files",
Long: `modifies the package and go_package names of proto files.
Long: color.HiBlackString(`modifies the package and go_package names of proto files.
Examples:
# modify the package and go_package names of all proto files in the api directory.
@ -32,8 +33,7 @@ Examples:
# modify the package and go_package names of all proto files in the api directory, get module name from docs/gen.
sponge patch modify-proto-package --dir=api --server-dir=server
`,
`),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
@ -129,43 +129,25 @@ func replaceProtoPackages(protoFilePath, packageName, goPackage string) error {
data = bytes.ReplaceAll(data, []byte("\r\n"), []byte("\n"))
}
regStr := `\npackage [\w\W]*?;`
reg := regexp.MustCompile(regStr)
srcPackageName := reg.Find(data)
regStr2 := `go_package [\w\W]*?;\n`
reg2 := regexp.MustCompile(regStr2)
srcGoPackageName := reg2.Find(data)
if len(srcPackageName) > 0 {
newPackage := fmt.Sprintf("\npackage %s;", packageName)
data = bytes.Replace(data, srcPackageName, []byte(newPackage), 1)
newGoPackage := fmt.Sprintf("go_package = %s;\n", goPackage)
if len(srcGoPackageName) > 0 {
data = bytes.Replace(data, srcGoPackageName, []byte(newGoPackage), 1)
} else {
data = bytes.Replace(data, []byte("\n\n"), []byte("\n\n"+newGoPackage+"\n\n"), 1)
}
if len(srcGoPackageName) > 0 {
newGoPackage := fmt.Sprintf("go_package = %s;\n", goPackage)
data = bytes.Replace(data, srcGoPackageName, []byte(newGoPackage), 1)
regStr := `\npackage [\w\W]*?;`
reg := regexp.MustCompile(regStr)
srcPackageName := reg.Find(data)
newPackage := fmt.Sprintf("\npackage %s;", packageName)
if len(srcPackageName) > 0 {
data = bytes.Replace(data, srcPackageName, []byte(newPackage), 1)
} else {
data = bytes.Replace(data, []byte("\n\n"), []byte("\n\n"+newPackage+"\n\n"), 1)
}
return os.WriteFile(protoFilePath, data, 0666)
}
// AddSpecialTypesCommand add common special types that proto files depend on
// Deprecated: This command has been discarded
func AddSpecialTypesCommand() *cobra.Command {
var dir string
cmd := &cobra.Command{
Use: "add-special-types",
Short: "Add common special types that proto files depend on, [Deprecated]",
Long: `add common special types that proto files depend on, this command has been deprecated.
`,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
return nil
},
}
cmd.Flags().StringVarP(&dir, "dir", "d", "", "input specified directory")
return cmd
}

3
go.mod
View File

@ -115,6 +115,7 @@ require (
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-redsync/redsync/v4 v4.12.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.1.2 // indirect
@ -122,7 +123,7 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect

6
go.sum
View File

@ -235,6 +235,10 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-redsync/redsync/v4 v4.12.1 h1:hCtdZ45DJxMxNdPiby5GlQwOKQmcka2587Y466qPqlA=
github.com/go-redsync/redsync/v4 v4.12.1/go.mod h1:sn72ojgeEhxUuRjrliK0NRrB0Zl6kOZ3BDvNN3P2jAY=
github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkvQ1EkZKA=
github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
@ -340,6 +344,8 @@ github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRr
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=

View File

@ -13,12 +13,17 @@ func init() {
}
func userExampleRouter(group *gin.RouterGroup, h handler.UserExampleHandler) {
//group.Use(middleware.Auth()) // all of the following routes use jwt authentication
// or group.Use(middleware.Auth(middleware.WithVerify(verify))) // token authentication
g := group.Group("/userExample")
group.POST("/userExample", h.Create)
group.DELETE("/userExample/:id", h.DeleteByID)
group.PUT("/userExample/:id", h.UpdateByID)
group.GET("/userExample/:id", h.GetByID)
group.POST("/userExample/list", h.List)
// All the following routes use jwt authentication, you also can use middleware.Auth(middleware.WithVerify(fn))
//g.Use(middleware.Auth())
// If jwt authentication is not required for all routes, authentication middleware can be added
// separately for only certain routes. In this case, g.Use(middleware.Auth()) above should not be used.
g.POST("/", h.Create) // [post] /api/v1/userExample
g.DELETE("/:id", h.DeleteByID) // [delete] /api/v1/userExample/:id
g.PUT("/:id", h.UpdateByID) // [put] /api/v1/userExample/:id
g.GET("/:id", h.GetByID) // [get] /api/v1/userExample/:id
g.POST("/list", h.List) // [post] /api/v1/userExample/list
}

View File

@ -13,17 +13,22 @@ func init() {
}
func userExampleRouter(group *gin.RouterGroup, h handler.UserExampleHandler) {
//group.Use(middleware.Auth()) // all of the following routes use jwt authentication
// or group.Use(middleware.Auth(middleware.WithVerify(verify))) // token authentication
g := group.Group("/userExample")
group.POST("/userExample", h.Create)
group.DELETE("/userExample/:id", h.DeleteByID)
group.PUT("/userExample/:id", h.UpdateByID)
group.GET("/userExample/:id", h.GetByID)
group.POST("/userExample/list", h.List)
// All the following routes use jwt authentication, you also can use middleware.Auth(middleware.WithVerify(fn))
//g.Use(middleware.Auth())
group.POST("/userExample/delete/ids", h.DeleteByIDs)
group.POST("/userExample/condition", h.GetByCondition)
group.POST("/userExample/list/ids", h.ListByIDs)
group.GET("/userExample/list", h.ListByLastID)
// If jwt authentication is not required for all routes, authentication middleware can be added
// separately for only certain routes. In this case, g.Use(middleware.Auth()) above should not be used.
g.POST("/", h.Create) // [post] /api/v1/userExample
g.DELETE("/:id", h.DeleteByID) // [delete] /api/v1/userExample/:id
g.PUT("/:id", h.UpdateByID) // [put] /api/v1/userExample/:id
g.GET("/:id", h.GetByID) // [get] /api/v1/userExample/:id
g.POST("/list", h.List) // [post] /api/v1/userExample/list
g.POST("/delete/ids", h.DeleteByIDs) // [post] /api/v1/userExample/delete/ids
g.POST("/condition", h.GetByCondition) // [post] /api/v1/userExample/condition
g.POST("/list/ids", h.ListByIDs) // [post] /api/v1/userExample/list/ids
g.GET("/list", h.ListByLastID) // [get] /api/v1/userExample/list
}

24
scripts/init.sh Normal file
View File

@ -0,0 +1,24 @@
#!/bin/bash
goModFile="go.mod"
thirdPartyProtoDir="third_party"
function checkResult() {
result=$1
if [ ${result} -ne 0 ]; then
exit ${result}
fi
}
if [ ! -f "../$goModFile" ]; then
sponge patch copy-go-mod
checkResult $?
mv -f go.mod ..
mv -f go.sum ..
fi
if [ ! -d "../$thirdPartyProtoDir" ]; then
sponge patch copy-third-party-proto
checkResult $?
mv -f $thirdPartyProtoDir ..
fi

View File

@ -7,6 +7,15 @@ allProtoFiles=""
specifiedProtoFilePath=$1
specifiedProtoFilePaths=""
colorGray='\033[1;30m'
colorGreen='\033[1;32m'
colorMagenta='\033[1;35m'
colorCyan='\033[1;36m'
highBright='\033[1m'
markEnd='\033[0m'
tipMsg=""
function checkResult() {
result=$1
if [ ${result} -ne 0 ]; then
@ -97,7 +106,7 @@ function generateByAllProto(){
echo "Error: not found proto file in path $protoBasePath"
exit 1
fi
echo "generate *pb.go by proto files: $allProtoFiles"
echo -e "generate *pb.go by proto files: ${colorGray}$allProtoFiles${markEnd}"
echo ""
# generate files *_pb.go
@ -153,7 +162,8 @@ function generateBySpecifiedProto(){
if [ "$specifiedProtoFiles"x = x ];then
return
fi
echo "generate template code by proto files: $specifiedProtoFiles"
echo -e "generate template code by proto files: ${colorMagenta}$specifiedProtoFiles${markEnd}"
echo ""
# todo generate api template code command here
# delete the templates code start
@ -180,17 +190,11 @@ function generateBySpecifiedProto(){
sponge merge rpc-gw-pb
checkResult $?
colorCyan='\033[1;36m'
highBright='\033[1m'
markEnd='\033[0m'
echo ""
echo -e "${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then visit ${colorCyan}http://localhost:8080/apis/swagger/index.html${markEnd} in your browser."
echo ""
tipMsg="${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then visit ${colorCyan}http://localhost:8080/apis/swagger/index.html${markEnd} in your browser."
# delete the templates code end
if [ "$suitedMonoRepo" == "true" ]; then
sponge patch adapt-mono-repo
sponge patch adapt-mono-repo --dir=serverNameExample
fi
}
@ -210,5 +214,7 @@ sponge patch del-omitempty --dir=$protoBasePath --suffix-name=pb.go > /dev/null
sponge patch modify-dup-num --dir=internal/ecode
sponge patch modify-dup-err-code --dir=internal/ecode
echo "generated code successfully."
echo -e "${colorGreen}generated code done.${markEnd}"
echo ""
echo -e $tipMsg
echo ""

View File

@ -25,6 +25,7 @@ checkResult $?
sponge patch modify-dup-num --dir=internal/ecode
sponge patch modify-dup-err-code --dir=internal/ecode
colorGreen='\033[1;32m'
colorCyan='\033[1;36m'
highBright='\033[1m'
markEnd='\033[0m'
@ -32,5 +33,5 @@ markEnd='\033[0m'
echo ""
echo -e "${highBright}Tip:${markEnd} execute the command ${colorCyan}make run${markEnd} and then visit ${colorCyan}http://${HOST_ADDR}:8080/swagger/index.html${markEnd} in your browser."
echo ""
echo "generated api docs successfully."
echo -e "${colorGreen}generated api docs done.${markEnd}"
echo ""

View File

@ -1,6 +1,7 @@
#!/bin/bash
rm -rf \
rm -rf go.mod go.sum \
third_party \
mono_01_http_mysql \
mono_02_http_postgresql \
mono_03_http_sqlite \

View File

@ -130,6 +130,7 @@ function runningProtoService() {
return 1
fi
make patch TYPE=types-pb
make proto
checkResult $?
echo -e "startup service $name"
@ -721,8 +722,6 @@ function generate_grpc_gw_pb() {
fi
cd $outDir
make copy-proto SERVER=../mono_05_grpc_mysql
checkResult $?
runningProtoService $serverName $outDir
checkResult $?
sleep 1