gomod.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package ctx
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
  12. "github.com/zeromicro/go-zero/tools/goctl/util/pathx"
  13. )
  14. const goModuleWithoutGoFiles = "command-line-arguments"
  15. var errInvalidGoMod = errors.New("invalid go module")
  16. // Module contains the relative data of go module,
  17. // which is the result of the command go list
  18. type Module struct {
  19. Path string
  20. Main bool
  21. Dir string
  22. GoMod string
  23. GoVersion string
  24. }
  25. func (m *Module) validate() error {
  26. if m.Path == goModuleWithoutGoFiles || m.Dir == "" {
  27. return errInvalidGoMod
  28. }
  29. return nil
  30. }
  31. // projectFromGoMod is used to find the go module and project file path
  32. // the workDir flag specifies which folder we need to detect based on
  33. // only valid for go mod project
  34. func projectFromGoMod(workDir string) (*ProjectContext, error) {
  35. if len(workDir) == 0 {
  36. return nil, errors.New("the work directory is not found")
  37. }
  38. if _, err := os.Stat(workDir); err != nil {
  39. return nil, err
  40. }
  41. workDir, err := pathx.ReadLink(workDir)
  42. if err != nil {
  43. return nil, err
  44. }
  45. m, err := getRealModule(workDir, execx.Run)
  46. if err != nil {
  47. return nil, err
  48. }
  49. if err := m.validate(); err != nil {
  50. return nil, err
  51. }
  52. var ret ProjectContext
  53. ret.WorkDir = workDir
  54. ret.Name = filepath.Base(m.Dir)
  55. dir, err := pathx.ReadLink(m.Dir)
  56. if err != nil {
  57. return nil, err
  58. }
  59. ret.Dir = dir
  60. ret.Path = m.Path
  61. return &ret, nil
  62. }
  63. func getRealModule(workDir string, execRun execx.RunFunc) (*Module, error) {
  64. data, err := execRun("go list -json -m", workDir)
  65. if err != nil {
  66. return nil, err
  67. }
  68. modules, err := decodePackages(strings.NewReader(data))
  69. if err != nil {
  70. return nil, err
  71. }
  72. for _, m := range modules {
  73. realDir, err := pathx.ReadLink(m.Dir)
  74. if err != nil {
  75. return nil, fmt.Errorf("failed to read go.mod, dir: %s, error: %w", m.Dir, err)
  76. }
  77. if strings.HasPrefix(workDir, realDir) {
  78. return &m, nil
  79. }
  80. }
  81. return nil, errors.New("no matched module")
  82. }
  83. func decodePackages(reader io.Reader) ([]Module, error) {
  84. br := bufio.NewReader(reader)
  85. if _, err := br.ReadSlice('{'); err != nil {
  86. return nil, err
  87. }
  88. if err := br.UnreadByte(); err != nil {
  89. return nil, err
  90. }
  91. var modules []Module
  92. decoder := json.NewDecoder(br)
  93. for decoder.More() {
  94. var m Module
  95. if err := decoder.Decode(&m); err != nil {
  96. return nil, fmt.Errorf("invalid module: %v", err)
  97. }
  98. modules = append(modules, m)
  99. }
  100. return modules, nil
  101. }