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.
244 lines
6.5 KiB
244 lines
6.5 KiB
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 |
|
}
|
|
|