go-common/app/tool/owner/main.go
2019-04-22 02:59:20 +00:00

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
}