feat: add gen_provider.go and gomod package
This commit is contained in:
@@ -10,6 +10,7 @@ func CommandGen(root *cobra.Command) {
|
|||||||
cmd.PersistentFlags().StringP("config", "c", "config.toml", "database config file")
|
cmd.PersistentFlags().StringP("config", "c", "config.toml", "database config file")
|
||||||
|
|
||||||
cmds := []func(*cobra.Command){
|
cmds := []func(*cobra.Command){
|
||||||
|
CommandGenProvider,
|
||||||
CommandGenModel,
|
CommandGenModel,
|
||||||
CommandGenEnum,
|
CommandGenEnum,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ func commandGenEnumE(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := os.ReadFile(path)
|
var content []byte
|
||||||
|
content, err = os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
436
cmd/gen_provider.go
Normal file
436
cmd/gen_provider.go
Normal file
@@ -0,0 +1,436 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io/fs"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"git.ipao.vip/rogeecn/atomctl/pkg/utils/gomod"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/tools/imports"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTypePkgName(typ string) string {
|
||||||
|
if strings.Contains(typ, ".") {
|
||||||
|
return strings.Split(typ, ".")[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandGenProvider(root *cobra.Command) {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "provider",
|
||||||
|
Short: "Generate providers",
|
||||||
|
Long: `
|
||||||
|
// @provider
|
||||||
|
// @provider:[except|only] [returnType] [group]
|
||||||
|
// when except add tag: inject:"false"
|
||||||
|
// when only add tag: inject:"true"
|
||||||
|
`,
|
||||||
|
RunE: commandGenProviderE,
|
||||||
|
}
|
||||||
|
|
||||||
|
root.AddCommand(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var scalarTypes = []string{
|
||||||
|
"float32",
|
||||||
|
"float64",
|
||||||
|
"int",
|
||||||
|
"int8",
|
||||||
|
"int16",
|
||||||
|
"int32",
|
||||||
|
"int64",
|
||||||
|
"uint",
|
||||||
|
"uint8",
|
||||||
|
"uint16",
|
||||||
|
"uint32",
|
||||||
|
"uint64",
|
||||||
|
"bool",
|
||||||
|
"uintptr",
|
||||||
|
"complex64",
|
||||||
|
"complex128",
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandGenProviderE(cmd *cobra.Command, args []string) error {
|
||||||
|
var err error
|
||||||
|
var path string
|
||||||
|
if len(args) > 0 {
|
||||||
|
path = args[0]
|
||||||
|
} else {
|
||||||
|
path, err = os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path, _ = filepath.Abs(path)
|
||||||
|
|
||||||
|
err = gomod.Parse(filepath.Join(path, "go.mod"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
providers := []Provider{}
|
||||||
|
|
||||||
|
// if path is file, then get the dir
|
||||||
|
log.Infof("generate providers for dir: %s", path)
|
||||||
|
// travel controller to find all controller objects
|
||||||
|
_ = filepath.WalkDir(path, func(filepath string, d fs.DirEntry, err error) error {
|
||||||
|
if d.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(filepath, ".go") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(filepath, "_test.go") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
providers = append(providers, astParseProviders(filepath)...)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// generate files
|
||||||
|
groups := lo.GroupBy(providers, func(item Provider) string {
|
||||||
|
return item.ProviderFile
|
||||||
|
})
|
||||||
|
|
||||||
|
for file, conf := range groups {
|
||||||
|
if err := renderFile(file, conf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type InjectParam struct {
|
||||||
|
Star string
|
||||||
|
Type string
|
||||||
|
Package string
|
||||||
|
PackageAlias string
|
||||||
|
}
|
||||||
|
type Provider struct {
|
||||||
|
StructName string
|
||||||
|
ReturnType string
|
||||||
|
ProviderGroup string
|
||||||
|
NeedPrepareFunc bool
|
||||||
|
InjectParams map[string]InjectParam
|
||||||
|
Imports map[string]string
|
||||||
|
PkgName string
|
||||||
|
ProviderFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func astParseProviders(source string) []Provider {
|
||||||
|
if strings.HasSuffix(source, "_test.go") {
|
||||||
|
return []Provider{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(source, "/provider.go") {
|
||||||
|
return []Provider{}
|
||||||
|
}
|
||||||
|
|
||||||
|
providers := []Provider{}
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
node, err := parser.ParseFile(fset, source, nil, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("ERR: ", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
imports := make(map[string]string)
|
||||||
|
for _, imp := range node.Imports {
|
||||||
|
name := ""
|
||||||
|
pkgPath := strings.Trim(imp.Path.Value, "\"")
|
||||||
|
|
||||||
|
if imp.Name != nil {
|
||||||
|
// 如果有显式指定包名,直接使用
|
||||||
|
name = imp.Name.Name
|
||||||
|
} else {
|
||||||
|
// 尝试从go.mod中获取真实包名
|
||||||
|
name = gomod.GetPackageModuleName(pkgPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理匿名导入的情况
|
||||||
|
if name == "_" {
|
||||||
|
name = gomod.GetPackageModuleName(pkgPath)
|
||||||
|
|
||||||
|
// 处理重名
|
||||||
|
if _, ok := imports[name]; ok {
|
||||||
|
name = fmt.Sprintf("%s%d", name, rand.Intn(100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imports[name] = pkgPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再去遍历 struct 的方法去
|
||||||
|
for _, decl := range node.Decls {
|
||||||
|
provider := Provider{
|
||||||
|
InjectParams: make(map[string]InjectParam),
|
||||||
|
Imports: make(map[string]string),
|
||||||
|
}
|
||||||
|
|
||||||
|
decl, ok := decl.(*ast.GenDecl)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decl.Specs) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
declType, ok := decl.Specs[0].(*ast.TypeSpec)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 必须包含注释 // @provider:only/except
|
||||||
|
if decl.Doc == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decl.Doc.List) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
structType, ok := declType.Type.(*ast.StructType)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
provider.StructName = declType.Name.Name
|
||||||
|
|
||||||
|
docMark := strings.TrimLeft(decl.Doc.List[len(decl.Doc.List)-1].Text, "/ \t")
|
||||||
|
if !strings.HasPrefix(docMark, "@provider") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mode, returnType, group := parseDoc(docMark)
|
||||||
|
if group != "" {
|
||||||
|
provider.ProviderGroup = group
|
||||||
|
}
|
||||||
|
// log.Infof("mode: %s, returnType: %s, group: %s", mode, returnType, group)
|
||||||
|
|
||||||
|
if returnType == "#" {
|
||||||
|
provider.ReturnType = "*" + provider.StructName
|
||||||
|
} else {
|
||||||
|
provider.ReturnType = returnType
|
||||||
|
}
|
||||||
|
onlyMode := mode == "only"
|
||||||
|
exceptMode := mode == "except"
|
||||||
|
log.Infof("[%s] %s => ONLY: %+v, EXCEPT: %+v, Type: %s, Group: %s", source, declType.Name.Name, onlyMode, exceptMode, provider.ReturnType, provider.ProviderGroup)
|
||||||
|
|
||||||
|
for _, field := range structType.Fields.List {
|
||||||
|
if field.Names == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.Tag != nil {
|
||||||
|
provider.NeedPrepareFunc = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if onlyMode {
|
||||||
|
if field.Tag == nil || !strings.Contains(field.Tag.Value, `inject:"true"`) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if exceptMode {
|
||||||
|
if field.Tag != nil && strings.Contains(field.Tag.Value, `inject:"false"`) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var star string
|
||||||
|
var pkg string
|
||||||
|
var pkgAlias string
|
||||||
|
var typ string
|
||||||
|
switch field.Type.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
typ = field.Type.(*ast.Ident).Name
|
||||||
|
case *ast.StarExpr:
|
||||||
|
star = "*"
|
||||||
|
paramsType := field.Type.(*ast.StarExpr)
|
||||||
|
switch paramsType.X.(type) {
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
X := paramsType.X.(*ast.SelectorExpr)
|
||||||
|
|
||||||
|
pkgAlias = X.X.(*ast.Ident).Name
|
||||||
|
p, ok := imports[pkgAlias]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkg = p
|
||||||
|
|
||||||
|
typ = X.Sel.Name
|
||||||
|
default:
|
||||||
|
typ = paramsType.X.(*ast.Ident).Name
|
||||||
|
}
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
pkgAlias = field.Type.(*ast.SelectorExpr).X.(*ast.Ident).Name
|
||||||
|
p, ok := imports[pkgAlias]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkg = p
|
||||||
|
typ = field.Type.(*ast.SelectorExpr).Sel.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo.Contains(scalarTypes, typ) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range field.Names {
|
||||||
|
provider.InjectParams[name.Name] = InjectParam{
|
||||||
|
Star: star,
|
||||||
|
Type: typ,
|
||||||
|
Package: pkg,
|
||||||
|
PackageAlias: pkgAlias,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if importPkg, ok := imports[pkgAlias]; ok {
|
||||||
|
provider.Imports[importPkg] = pkgAlias
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgAlias := getTypePkgName(provider.ReturnType); pkgAlias != "" {
|
||||||
|
if importPkg, ok := imports[pkgAlias]; ok {
|
||||||
|
provider.Imports[importPkg] = pkgAlias
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgAlias := getTypePkgName(provider.ProviderGroup); pkgAlias != "" {
|
||||||
|
if importPkg, ok := imports[pkgAlias]; ok {
|
||||||
|
provider.Imports[importPkg] = pkgAlias
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider.PkgName = node.Name.Name
|
||||||
|
provider.ProviderFile = filepath.Join(filepath.Dir(source), "provider.gen.go")
|
||||||
|
|
||||||
|
providers = append(providers, provider)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDoc(doc string) (string, string, string) {
|
||||||
|
// @provider:[except|only] [returnType] [group]
|
||||||
|
doc = strings.TrimLeft(doc[len("@provider"):], ":")
|
||||||
|
if !strings.HasPrefix(doc, "except") && !strings.HasPrefix(doc, "only") {
|
||||||
|
doc = "except " + doc
|
||||||
|
}
|
||||||
|
|
||||||
|
doc = strings.ReplaceAll(doc, "\t", " ")
|
||||||
|
cmds := strings.Split(doc, " ")
|
||||||
|
cmds = lo.Filter(cmds, func(item string, idx int) bool {
|
||||||
|
return strings.TrimSpace(item) != ""
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(cmds) == 0 {
|
||||||
|
return "except", "#", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmds) == 1 {
|
||||||
|
return cmds[0], "#", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmds) == 2 {
|
||||||
|
return cmds[0], cmds[1], ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmds[0], cmds[1], cmds[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderFile(filename string, conf []Provider) error {
|
||||||
|
defer func() {
|
||||||
|
result, err := imports.Process(filename, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
os.WriteFile(filename, result, os.ModePerm)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
imports := map[string]string{
|
||||||
|
"git.ipao.vip/rogeecn/atom/container": "",
|
||||||
|
"git.ipao.vip/rogeecn/atom/utils/opt": "",
|
||||||
|
}
|
||||||
|
lo.ForEach(conf, func(item Provider, _ int) {
|
||||||
|
for k, v := range item.Imports {
|
||||||
|
// 如果是当前包的引用,直接使用包名
|
||||||
|
if strings.HasSuffix(k, "/"+v) {
|
||||||
|
v = ""
|
||||||
|
}
|
||||||
|
imports[k] = v
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
tmpl := `package {{.PkgName}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
{{- range $pkg, $alias := .Imports }}
|
||||||
|
{{- if eq $alias "" }}
|
||||||
|
"{{$pkg}}"
|
||||||
|
{{- else }}
|
||||||
|
{{$alias}} "{{$pkg}}"
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Provide(opts ...opt.Option) error {
|
||||||
|
{{- range .Providers }}
|
||||||
|
if err := container.Container.Provide(func(
|
||||||
|
{{- range $key, $param := .InjectParams }}
|
||||||
|
{{$key}} {{$param.Star}}{{if eq $param.Package ""}}{{$param.Type}}{{else}}{{$param.PackageAlias}}.{{$param.Type}}{{end}},
|
||||||
|
{{- end }}
|
||||||
|
) ({{.ReturnType}}, error) {
|
||||||
|
obj := &{{.StructName}}{
|
||||||
|
{{- range $key, $param := .InjectParams }}
|
||||||
|
{{$key}}: {{$key}},
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
{{- if .NeedPrepareFunc }}
|
||||||
|
if err := obj.Prepare(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
return obj, nil
|
||||||
|
}{{if .ProviderGroup}}, {{.ProviderGroup}}{{end}}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
t := template.Must(template.New("provider").Parse(tmpl))
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
PkgName string
|
||||||
|
Imports map[string]string
|
||||||
|
Providers []Provider
|
||||||
|
}{
|
||||||
|
PkgName: conf[0].PkgName,
|
||||||
|
Imports: imports,
|
||||||
|
Providers: conf,
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_RDWR, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
return t.Execute(fd, data)
|
||||||
|
}
|
||||||
7
go.mod
7
go.mod
@@ -9,11 +9,14 @@ require (
|
|||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pressly/goose/v3 v3.23.1
|
github.com/pressly/goose/v3 v3.23.1
|
||||||
|
github.com/rogeecn/fabfile v1.4.0
|
||||||
github.com/samber/lo v1.47.0
|
github.com/samber/lo v1.47.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
github.com/smartystreets/goconvey v1.8.1
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
|
golang.org/x/mod v0.17.0
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.21.0
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
|
||||||
)
|
)
|
||||||
@@ -25,6 +28,7 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/huandu/xstrings v1.5.0 // indirect
|
github.com/huandu/xstrings v1.5.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
@@ -35,6 +39,7 @@ require (
|
|||||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/pgtype v1.14.4 // indirect
|
github.com/jackc/pgtype v1.14.4 // indirect
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mfridman/interpolate v0.0.2 // indirect
|
github.com/mfridman/interpolate v0.0.2 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
@@ -46,6 +51,7 @@ require (
|
|||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/sethvargo/go-retry v0.3.0 // indirect
|
github.com/sethvargo/go-retry v0.3.0 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
|
github.com/smarty/assertions v1.15.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.7.0 // indirect
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
@@ -54,7 +60,6 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
|
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
|||||||
10
go.sum
10
go.sum
@@ -36,6 +36,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
|||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
@@ -96,6 +98,8 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f
|
|||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@@ -144,6 +148,8 @@ github.com/pressly/goose/v3 v3.23.1 h1:bwjOXvep4HtuiiIqtrXmCkQu0IW9O9JAqA6UQNY9n
|
|||||||
github.com/pressly/goose/v3 v3.23.1/go.mod h1:0oK0zcK7cmNqJSVwMIOiUUW0ox2nDIz+UfPMSOaw2zY=
|
github.com/pressly/goose/v3 v3.23.1/go.mod h1:0oK0zcK7cmNqJSVwMIOiUUW0ox2nDIz+UfPMSOaw2zY=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/rogeecn/fabfile v1.4.0 h1:Rw7/7OH8cV4aRPw79Oa4hHHFKaC/ol+sNmGcB/usHaQ=
|
||||||
|
github.com/rogeecn/fabfile v1.4.0/go.mod h1:EPwX7TtVcIWSLJkJAqxSzYjM/aV1Q0wymcaXqnMgzas=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
@@ -168,6 +174,10 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
|||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||||
|
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||||
|
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||||
|
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
|
|||||||
142
pkg/utils/gomod/mod.go
Normal file
142
pkg/utils/gomod/mod.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package gomod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
var goMod *GoMod
|
||||||
|
|
||||||
|
type GoMod struct {
|
||||||
|
file *modfile.File
|
||||||
|
modules map[string]ModuleInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModuleInfo struct {
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseGoMod 解析当前目录下的go.mod文件
|
||||||
|
func Parse(modPath string) error {
|
||||||
|
// 查找当前目录下的go.mod文件
|
||||||
|
// 读取文件内容
|
||||||
|
content, err := os.ReadFile(modPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用官方包解析go.mod
|
||||||
|
f, err := modfile.Parse(modPath, content, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
goMod = &GoMod{file: f, modules: make(map[string]ModuleInfo)}
|
||||||
|
|
||||||
|
for _, require := range f.Require {
|
||||||
|
if !require.Indirect {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := getPackageName(require.Mod.Path, require.Mod.Version)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
goMod.modules[require.Mod.Path] = ModuleInfo{
|
||||||
|
Name: name,
|
||||||
|
Version: require.Mod.Version,
|
||||||
|
Path: require.Mod.Path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetModuleName 获取模块名
|
||||||
|
func GetModuleName() string {
|
||||||
|
return goMod.file.Module.Mod.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetModuleVersion 获取模块版本
|
||||||
|
func GetModuleVersion() string {
|
||||||
|
return goMod.file.Module.Mod.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPackageModuleName(pkg string) string {
|
||||||
|
if module, ok := goMod.modules[pkg]; ok {
|
||||||
|
return module.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Base(pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPackageModuleName 获取包的真实包名
|
||||||
|
func getPackageName(pkg, version string) (string, error) {
|
||||||
|
gopath := os.Getenv("GOPATH")
|
||||||
|
if gopath == "" {
|
||||||
|
gopath = filepath.Join(os.Getenv("HOME"), "go")
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgPath := fmt.Sprintf("%s@%s", pkg, version)
|
||||||
|
// 构建包的本地路径
|
||||||
|
pkgLocalPath := filepath.Join(gopath, "pkg", "mod", pkgPath)
|
||||||
|
|
||||||
|
// 获取目录下任意一个非_test.go文件,读取他的package name
|
||||||
|
files, err := filepath.Glob(filepath.Join(pkgLocalPath, "*.go"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
packagePattern := regexp.MustCompile(`package\s+(\w+)`)
|
||||||
|
if len(files) > 0 {
|
||||||
|
for _, file := range files {
|
||||||
|
if strings.HasSuffix(file, "_test.go") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 读取文件内容
|
||||||
|
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
packageName := packagePattern.FindStringSubmatch(string(content))
|
||||||
|
if len(packageName) == 2 {
|
||||||
|
return packageName[1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 读取go.mod 文件内容
|
||||||
|
modFile := filepath.Join(pkgLocalPath, "go.mod")
|
||||||
|
content, err := os.ReadFile(modFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := modfile.Parse(modFile, content, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
path := f.Module.Mod.Path
|
||||||
|
|
||||||
|
// 获取包名
|
||||||
|
path, name := filepath.Split(path)
|
||||||
|
versionPattern := regexp.MustCompile(`^v\d+$`)
|
||||||
|
if versionPattern.MatchString(name) {
|
||||||
|
_, name = filepath.Split(strings.TrimSuffix(path, "/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(name, "-") {
|
||||||
|
name = strings.ReplaceAll(name, "-", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
32
pkg/utils/gomod/mod_test.go
Normal file
32
pkg/utils/gomod/mod_test.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package gomod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rogeecn/fabfile"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ParseGoMod(t *testing.T) {
|
||||||
|
Convey("Test ParseGoMod", t, func() {
|
||||||
|
Convey("parse go.mod", func() {
|
||||||
|
modFile := fabfile.MustFind("go.mod")
|
||||||
|
err := Parse(modFile)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
t.Logf("%+v", goMod)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getPackageName(t *testing.T) {
|
||||||
|
Convey("Test getPackageName", t, func() {
|
||||||
|
Convey("", func() {
|
||||||
|
Convey("github.com/redis/go-redis/v9@v9.7.0", func() {
|
||||||
|
name, err := getPackageName("github.com/redis/go-redis/v9", "v9.7.0")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(name, ShouldEqual, "redis")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user