You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

modules.go 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package config
  2. import (
  3. "bytes"
  4. "errors"
  5. "regexp"
  6. format "github.com/go-git/go-git/v5/plumbing/format/config"
  7. )
  8. var (
  9. ErrModuleEmptyURL = errors.New("module config: empty URL")
  10. ErrModuleEmptyPath = errors.New("module config: empty path")
  11. ErrModuleBadPath = errors.New("submodule has an invalid path")
  12. )
  13. var (
  14. // Matches module paths with dotdot ".." components.
  15. dotdotPath = regexp.MustCompile(`(^|[/\\])\.\.([/\\]|$)`)
  16. )
  17. // Modules defines the submodules properties, represents a .gitmodules file
  18. // https://www.kernel.org/pub/software/scm/git/docs/gitmodules.html
  19. type Modules struct {
  20. // Submodules is a map of submodules being the key the name of the submodule.
  21. Submodules map[string]*Submodule
  22. raw *format.Config
  23. }
  24. // NewModules returns a new empty Modules
  25. func NewModules() *Modules {
  26. return &Modules{
  27. Submodules: make(map[string]*Submodule),
  28. raw: format.New(),
  29. }
  30. }
  31. const (
  32. pathKey = "path"
  33. branchKey = "branch"
  34. )
  35. // Unmarshal parses a git-config file and stores it.
  36. func (m *Modules) Unmarshal(b []byte) error {
  37. r := bytes.NewBuffer(b)
  38. d := format.NewDecoder(r)
  39. m.raw = format.New()
  40. if err := d.Decode(m.raw); err != nil {
  41. return err
  42. }
  43. unmarshalSubmodules(m.raw, m.Submodules)
  44. return nil
  45. }
  46. // Marshal returns Modules encoded as a git-config file.
  47. func (m *Modules) Marshal() ([]byte, error) {
  48. s := m.raw.Section(submoduleSection)
  49. s.Subsections = make(format.Subsections, len(m.Submodules))
  50. var i int
  51. for _, r := range m.Submodules {
  52. s.Subsections[i] = r.marshal()
  53. i++
  54. }
  55. buf := bytes.NewBuffer(nil)
  56. if err := format.NewEncoder(buf).Encode(m.raw); err != nil {
  57. return nil, err
  58. }
  59. return buf.Bytes(), nil
  60. }
  61. // Submodule defines a submodule.
  62. type Submodule struct {
  63. // Name module name
  64. Name string
  65. // Path defines the path, relative to the top-level directory of the Git
  66. // working tree.
  67. Path string
  68. // URL defines a URL from which the submodule repository can be cloned.
  69. URL string
  70. // Branch is a remote branch name for tracking updates in the upstream
  71. // submodule. Optional value.
  72. Branch string
  73. // raw representation of the subsection, filled by marshal or unmarshal are
  74. // called.
  75. raw *format.Subsection
  76. }
  77. // Validate validates the fields and sets the default values.
  78. func (m *Submodule) Validate() error {
  79. if m.Path == "" {
  80. return ErrModuleEmptyPath
  81. }
  82. if m.URL == "" {
  83. return ErrModuleEmptyURL
  84. }
  85. if dotdotPath.MatchString(m.Path) {
  86. return ErrModuleBadPath
  87. }
  88. return nil
  89. }
  90. func (m *Submodule) unmarshal(s *format.Subsection) {
  91. m.raw = s
  92. m.Name = m.raw.Name
  93. m.Path = m.raw.Option(pathKey)
  94. m.URL = m.raw.Option(urlKey)
  95. m.Branch = m.raw.Option(branchKey)
  96. }
  97. func (m *Submodule) marshal() *format.Subsection {
  98. if m.raw == nil {
  99. m.raw = &format.Subsection{}
  100. }
  101. m.raw.Name = m.Name
  102. if m.raw.Name == "" {
  103. m.raw.Name = m.Path
  104. }
  105. m.raw.SetOption(pathKey, m.Path)
  106. m.raw.SetOption(urlKey, m.URL)
  107. if m.Branch != "" {
  108. m.raw.SetOption(branchKey, m.Branch)
  109. }
  110. return m.raw
  111. }