main.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "github.com/ghodss/yaml"
  9. "k8s.io/apimachinery/pkg/util/sets"
  10. )
  11. type DirOptions struct {
  12. NoParentOwners bool `json:"no_parent_owners,omitempty"`
  13. }
  14. type Config struct {
  15. Approvers []string `json:"approvers,omitempty"`
  16. Reviewers []string `json:"reviewers,omitempty"`
  17. RequiredReviewers []string `json:"required_reviewers,omitempty"`
  18. Labels []string `json:"labels,omitempty"`
  19. Options *DirOptions `json:"options,omitempty"`
  20. }
  21. // Empty checks if a SimpleConfig could be considered empty
  22. func (s *Config) Empty() bool {
  23. return len(s.Approvers) == 0 && len(s.Reviewers) == 0 && len(s.RequiredReviewers) == 0 && len(s.Labels) == 0
  24. }
  25. type Owner struct {
  26. Config `json:",inline"`
  27. }
  28. type contributor struct {
  29. Owner []string
  30. Author []string
  31. Reviewer []string
  32. }
  33. // FullConfig contains Filters which apply specific Config to files matching its regexp
  34. type FullConfig struct {
  35. Options DirOptions `json:"options,omitempty"`
  36. Filters map[string]Config `json:"filters,omitempty"`
  37. }
  38. func readContributor(content []byte) (c *contributor) {
  39. var (
  40. lines []string
  41. lineStr string
  42. curSection string
  43. )
  44. c = &contributor{}
  45. lines = strings.Split(string(content), "\n")
  46. for _, lineStr = range lines {
  47. if lineStr == "" {
  48. continue
  49. }
  50. if strings.Contains(strings.ToLower(lineStr), "owner") {
  51. curSection = "owner"
  52. continue
  53. }
  54. if strings.Contains(strings.ToLower(lineStr), "author") {
  55. curSection = "author"
  56. continue
  57. }
  58. if strings.Contains(strings.ToLower(lineStr), "reviewer") {
  59. curSection = "reviewer"
  60. continue
  61. }
  62. switch curSection {
  63. case "owner":
  64. c.Owner = append(c.Owner, strings.TrimSpace(lineStr))
  65. case "author":
  66. c.Author = append(c.Author, strings.TrimSpace(lineStr))
  67. case "reviewer":
  68. c.Reviewer = append(c.Reviewer, strings.TrimSpace(lineStr))
  69. }
  70. }
  71. return
  72. }
  73. func Label(path string) string {
  74. var result string
  75. if filepath.HasPrefix(path, "app") {
  76. path = strings.Replace(path, "/CONTRIBUTORS.md", "", 1)
  77. path = strings.Replace(path, "/OWNERS", "", 1)
  78. path = strings.Replace(path, "app/", "", 1)
  79. if len(strings.Split(path, "/")) == 3 {
  80. result = path
  81. } else {
  82. if len(strings.Split(path, "/")) == 2 && filepath.HasPrefix(path, "infra") {
  83. result = path
  84. }
  85. }
  86. }
  87. return result
  88. }
  89. func main() {
  90. filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
  91. if err != nil {
  92. fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err)
  93. return err
  94. }
  95. if filepath.HasPrefix(path, "vendor") && filepath.HasPrefix(path, "build") {
  96. return filepath.SkipDir
  97. }
  98. if path == "CONTRIBUTORS.md" {
  99. return nil
  100. }
  101. if !info.IsDir() && info.Name() == "CONTRIBUTORS.md" || info.Name() == "OWNERS" {
  102. owner := Owner{}
  103. approves := sets.NewString()
  104. reviewers := sets.NewString()
  105. labels := sets.NewString()
  106. if info.Name() == "CONTRIBUTORS.md" {
  107. content, err := ioutil.ReadFile(path)
  108. if err != nil {
  109. fmt.Printf("fail to read contributor %q: %v\n", path, err)
  110. return err
  111. }
  112. c := readContributor(content)
  113. approves.Insert(c.Owner...)
  114. reviewers.Insert(c.Author...)
  115. reviewers.Insert(c.Reviewer...)
  116. }
  117. if strings.Contains(path, "/main/") {
  118. labels.Insert("main")
  119. } else {
  120. if strings.Contains(path, "/ep/") {
  121. labels.Insert("ep")
  122. } else {
  123. if strings.Contains(path, "/live/") {
  124. labels.Insert("live")
  125. } else {
  126. if strings.Contains(path, "/openplatform/") {
  127. labels.Insert("openplatform")
  128. } else {
  129. if strings.Contains(path, "bbq/") {
  130. labels.Insert("bbq")
  131. }
  132. }
  133. }
  134. }
  135. }
  136. if strings.Contains(path, "admin/") {
  137. labels.Insert("admin")
  138. } else {
  139. if strings.Contains(path, "common/") {
  140. labels.Insert("common")
  141. } else {
  142. if strings.Contains(path, "interface/") {
  143. labels.Insert("interface")
  144. } else {
  145. if strings.Contains(path, "job/") {
  146. labels.Insert("job")
  147. } else {
  148. if strings.Contains(path, "service/") {
  149. labels.Insert("service")
  150. } else {
  151. if strings.Contains(path, "infra/") {
  152. labels.Insert("infra")
  153. } else {
  154. if strings.Contains(path, "tool/") {
  155. labels.Insert("tool")
  156. }
  157. }
  158. }
  159. }
  160. }
  161. }
  162. }
  163. oldyaml, err := ioutil.ReadFile(strings.Replace(path, "CONTRIBUTORS.md", "OWNERS", -1))
  164. if err == nil {
  165. var owner Owner
  166. err = yaml.Unmarshal(oldyaml, &owner)
  167. if err != nil || owner.Empty() {
  168. c, err := ParseFullConfig(oldyaml)
  169. if err != nil {
  170. return err
  171. }
  172. data, err := yaml.Marshal(c)
  173. if err != nil {
  174. fmt.Printf("fail to Marshal %q: %v\n", path, err)
  175. return nil
  176. }
  177. data = append([]byte("# See the OWNERS docs at https://go.k8s.io/owners\n\n"), data...)
  178. ownerpath := strings.Replace(path, "CONTRIBUTORS.md", "OWNERS", 1)
  179. err = ioutil.WriteFile(ownerpath, data, 0644)
  180. if err != nil {
  181. fmt.Printf("fail to write yaml %q: %v\n", path, err)
  182. return err
  183. }
  184. return nil
  185. }
  186. approves.Insert(owner.Approvers...)
  187. reviewers.Insert(owner.Reviewers...)
  188. labels.Insert(owner.Labels...)
  189. }
  190. labels.Insert(Label(path))
  191. approves.Delete("all", "")
  192. reviewers.Delete("all", "")
  193. labels.Delete("all", "")
  194. owner.Approvers = approves.List()
  195. owner.Reviewers = reviewers.List()
  196. owner.Labels = labels.List()
  197. if strings.Contains(path, "app") && len(strings.Split(path, "/")) > 4 {
  198. owner.Options = &DirOptions{}
  199. owner.Options.NoParentOwners = true
  200. }
  201. if strings.Contains(path, "library/ecode") || strings.Contains(path, "app/tool") || strings.Contains(path, "app/infra") && len(strings.Split(path, "/")) > 2 {
  202. owner.Options = &DirOptions{}
  203. owner.Options.NoParentOwners = true
  204. }
  205. data, err := yaml.Marshal(owner)
  206. if err != nil {
  207. fmt.Printf("fail to Marshal %q: %v\n", path, err)
  208. return nil
  209. }
  210. data = append([]byte("# See the OWNERS docs at https://go.k8s.io/owners\n\n"), data...)
  211. ownerpath := strings.Replace(path, "CONTRIBUTORS.md", "OWNERS", 1)
  212. err = ioutil.WriteFile(ownerpath, data, 0644)
  213. if err != nil {
  214. fmt.Printf("fail to write yaml %q: %v\n", path, err)
  215. return err
  216. }
  217. return nil
  218. }
  219. return nil
  220. })
  221. }
  222. // ParseFullConfig will unmarshal OWNERS file's content into a FullConfig
  223. // Returns an error if the content cannot be unmarshalled
  224. func ParseFullConfig(b []byte) (FullConfig, error) {
  225. full := new(FullConfig)
  226. err := yaml.Unmarshal(b, full)
  227. return *full, err
  228. }