245 lines
6.5 KiB
Go
245 lines
6.5 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/ghodss/yaml"
|
||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||
|
)
|
||
|
|
||
|
type DirOptions struct {
|
||
|
NoParentOwners bool `json:"no_parent_owners,omitempty"`
|
||
|
}
|
||
|
|
||
|
type Config struct {
|
||
|
Approvers []string `json:"approvers,omitempty"`
|
||
|
Reviewers []string `json:"reviewers,omitempty"`
|
||
|
RequiredReviewers []string `json:"required_reviewers,omitempty"`
|
||
|
Labels []string `json:"labels,omitempty"`
|
||
|
Options *DirOptions `json:"options,omitempty"`
|
||
|
}
|
||
|
|
||
|
// Empty checks if a SimpleConfig could be considered empty
|
||
|
func (s *Config) Empty() bool {
|
||
|
return len(s.Approvers) == 0 && len(s.Reviewers) == 0 && len(s.RequiredReviewers) == 0 && len(s.Labels) == 0
|
||
|
}
|
||
|
|
||
|
type Owner struct {
|
||
|
Config `json:",inline"`
|
||
|
}
|
||
|
|
||
|
type contributor struct {
|
||
|
Owner []string
|
||
|
Author []string
|
||
|
Reviewer []string
|
||
|
}
|
||
|
|
||
|
// FullConfig contains Filters which apply specific Config to files matching its regexp
|
||
|
type FullConfig struct {
|
||
|
Options DirOptions `json:"options,omitempty"`
|
||
|
Filters map[string]Config `json:"filters,omitempty"`
|
||
|
}
|
||
|
|
||
|
func readContributor(content []byte) (c *contributor) {
|
||
|
var (
|
||
|
lines []string
|
||
|
lineStr string
|
||
|
curSection string
|
||
|
)
|
||
|
c = &contributor{}
|
||
|
lines = strings.Split(string(content), "\n")
|
||
|
for _, lineStr = range lines {
|
||
|
if lineStr == "" {
|
||
|
continue
|
||
|
}
|
||
|
if strings.Contains(strings.ToLower(lineStr), "owner") {
|
||
|
curSection = "owner"
|
||
|
continue
|
||
|
}
|
||
|
if strings.Contains(strings.ToLower(lineStr), "author") {
|
||
|
curSection = "author"
|
||
|
continue
|
||
|
}
|
||
|
if strings.Contains(strings.ToLower(lineStr), "reviewer") {
|
||
|
curSection = "reviewer"
|
||
|
continue
|
||
|
}
|
||
|
switch curSection {
|
||
|
case "owner":
|
||
|
c.Owner = append(c.Owner, strings.TrimSpace(lineStr))
|
||
|
case "author":
|
||
|
c.Author = append(c.Author, strings.TrimSpace(lineStr))
|
||
|
case "reviewer":
|
||
|
c.Reviewer = append(c.Reviewer, strings.TrimSpace(lineStr))
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func Label(path string) string {
|
||
|
var result string
|
||
|
if filepath.HasPrefix(path, "app") {
|
||
|
path = strings.Replace(path, "/CONTRIBUTORS.md", "", 1)
|
||
|
path = strings.Replace(path, "/OWNERS", "", 1)
|
||
|
path = strings.Replace(path, "app/", "", 1)
|
||
|
if len(strings.Split(path, "/")) == 3 {
|
||
|
result = path
|
||
|
} else {
|
||
|
if len(strings.Split(path, "/")) == 2 && filepath.HasPrefix(path, "infra") {
|
||
|
result = path
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
|
||
|
if err != nil {
|
||
|
fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err)
|
||
|
return err
|
||
|
}
|
||
|
if filepath.HasPrefix(path, "vendor") && filepath.HasPrefix(path, "build") {
|
||
|
return filepath.SkipDir
|
||
|
}
|
||
|
if path == "CONTRIBUTORS.md" {
|
||
|
return nil
|
||
|
}
|
||
|
if !info.IsDir() && info.Name() == "CONTRIBUTORS.md" || info.Name() == "OWNERS" {
|
||
|
owner := Owner{}
|
||
|
approves := sets.NewString()
|
||
|
reviewers := sets.NewString()
|
||
|
labels := sets.NewString()
|
||
|
if info.Name() == "CONTRIBUTORS.md" {
|
||
|
content, err := ioutil.ReadFile(path)
|
||
|
if err != nil {
|
||
|
fmt.Printf("fail to read contributor %q: %v\n", path, err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
c := readContributor(content)
|
||
|
|
||
|
approves.Insert(c.Owner...)
|
||
|
reviewers.Insert(c.Author...)
|
||
|
reviewers.Insert(c.Reviewer...)
|
||
|
}
|
||
|
|
||
|
if strings.Contains(path, "/main/") {
|
||
|
labels.Insert("main")
|
||
|
} else {
|
||
|
if strings.Contains(path, "/ep/") {
|
||
|
labels.Insert("ep")
|
||
|
} else {
|
||
|
if strings.Contains(path, "/live/") {
|
||
|
labels.Insert("live")
|
||
|
} else {
|
||
|
if strings.Contains(path, "/openplatform/") {
|
||
|
labels.Insert("openplatform")
|
||
|
} else {
|
||
|
if strings.Contains(path, "bbq/") {
|
||
|
labels.Insert("bbq")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if strings.Contains(path, "admin/") {
|
||
|
labels.Insert("admin")
|
||
|
} else {
|
||
|
if strings.Contains(path, "common/") {
|
||
|
labels.Insert("common")
|
||
|
} else {
|
||
|
if strings.Contains(path, "interface/") {
|
||
|
labels.Insert("interface")
|
||
|
} else {
|
||
|
if strings.Contains(path, "job/") {
|
||
|
labels.Insert("job")
|
||
|
} else {
|
||
|
if strings.Contains(path, "service/") {
|
||
|
labels.Insert("service")
|
||
|
} else {
|
||
|
if strings.Contains(path, "infra/") {
|
||
|
labels.Insert("infra")
|
||
|
} else {
|
||
|
if strings.Contains(path, "tool/") {
|
||
|
labels.Insert("tool")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
oldyaml, err := ioutil.ReadFile(strings.Replace(path, "CONTRIBUTORS.md", "OWNERS", -1))
|
||
|
if err == nil {
|
||
|
var owner Owner
|
||
|
err = yaml.Unmarshal(oldyaml, &owner)
|
||
|
if err != nil || owner.Empty() {
|
||
|
c, err := ParseFullConfig(oldyaml)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
data, err := yaml.Marshal(c)
|
||
|
if err != nil {
|
||
|
fmt.Printf("fail to Marshal %q: %v\n", path, err)
|
||
|
return nil
|
||
|
}
|
||
|
data = append([]byte("# See the OWNERS docs at https://go.k8s.io/owners\n\n"), data...)
|
||
|
ownerpath := strings.Replace(path, "CONTRIBUTORS.md", "OWNERS", 1)
|
||
|
err = ioutil.WriteFile(ownerpath, data, 0644)
|
||
|
if err != nil {
|
||
|
fmt.Printf("fail to write yaml %q: %v\n", path, err)
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
approves.Insert(owner.Approvers...)
|
||
|
reviewers.Insert(owner.Reviewers...)
|
||
|
labels.Insert(owner.Labels...)
|
||
|
}
|
||
|
labels.Insert(Label(path))
|
||
|
approves.Delete("all", "")
|
||
|
reviewers.Delete("all", "")
|
||
|
labels.Delete("all", "")
|
||
|
owner.Approvers = approves.List()
|
||
|
owner.Reviewers = reviewers.List()
|
||
|
owner.Labels = labels.List()
|
||
|
if strings.Contains(path, "app") && len(strings.Split(path, "/")) > 4 {
|
||
|
owner.Options = &DirOptions{}
|
||
|
owner.Options.NoParentOwners = true
|
||
|
}
|
||
|
if strings.Contains(path, "library/ecode") || strings.Contains(path, "app/tool") || strings.Contains(path, "app/infra") && len(strings.Split(path, "/")) > 2 {
|
||
|
owner.Options = &DirOptions{}
|
||
|
owner.Options.NoParentOwners = true
|
||
|
}
|
||
|
data, err := yaml.Marshal(owner)
|
||
|
if err != nil {
|
||
|
fmt.Printf("fail to Marshal %q: %v\n", path, err)
|
||
|
return nil
|
||
|
}
|
||
|
data = append([]byte("# See the OWNERS docs at https://go.k8s.io/owners\n\n"), data...)
|
||
|
ownerpath := strings.Replace(path, "CONTRIBUTORS.md", "OWNERS", 1)
|
||
|
err = ioutil.WriteFile(ownerpath, data, 0644)
|
||
|
if err != nil {
|
||
|
fmt.Printf("fail to write yaml %q: %v\n", path, err)
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
return nil
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// ParseFullConfig will unmarshal OWNERS file's content into a FullConfig
|
||
|
// Returns an error if the content cannot be unmarshalled
|
||
|
func ParseFullConfig(b []byte) (FullConfig, error) {
|
||
|
full := new(FullConfig)
|
||
|
err := yaml.Unmarshal(b, full)
|
||
|
return *full, err
|
||
|
}
|