123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- // Copyright The OpenTelemetry Authors
- // SPDX-License-Identifier: Apache-2.0
- package main
- import (
- "context"
- "flag"
- "fmt"
- "io/fs"
- "log"
- "path/filepath"
- "sort"
- "go.opentelemetry.io/collector/confmap/provider/fileprovider"
- )
- const unmaintainedStatus = "unmaintained"
- type generator interface {
- generate(data *githubData) error
- }
- // Generates files specific to Github according to status metadata:
- // .github/CODEOWNERS
- // .github/ALLOWLIST
- // .github/ISSUE_TEMPLATES/*.yaml (list of components)
- func main() {
- folder := flag.String("folder", ".", "folder investigated for codeowners")
- allowlistFilePath := flag.String("allowlist", "cmd/githubgen/allowlist.txt", "path to a file containing an allowlist of members outside the OpenTelemetry organization")
- flag.Parse()
- var generators []generator
- for _, arg := range flag.Args() {
- switch arg {
- case "issue-templates":
- generators = append(generators, issueTemplatesGenerator{})
- case "codeowners":
- generators = append(generators, codeownersGenerator{})
- default:
- panic(fmt.Sprintf("Unknown generator: %s", arg))
- }
- }
- if len(generators) == 0 {
- generators = []generator{issueTemplatesGenerator{}, codeownersGenerator{}}
- }
- if err := run(*folder, *allowlistFilePath, generators); err != nil {
- log.Fatal(err)
- }
- }
- type codeowners struct {
- // Active codeowners
- Active []string `mapstructure:"active"`
- // Emeritus codeowners
- Emeritus []string `mapstructure:"emeritus"`
- }
- type Status struct {
- Stability map[string][]string `mapstructure:"stability"`
- Distributions []string `mapstructure:"distributions"`
- Class string `mapstructure:"class"`
- Warnings []string `mapstructure:"warnings"`
- Codeowners *codeowners `mapstructure:"codeowners"`
- }
- type metadata struct {
- // Type of the component.
- Type string `mapstructure:"type"`
- // Type of the parent component (applicable to subcomponents).
- Parent string `mapstructure:"parent"`
- // Status information for the component.
- Status *Status `mapstructure:"status"`
- }
- type githubData struct {
- folders []string
- codeowners []string
- allowlistFilePath string
- maxLength int
- components map[string]metadata
- }
- func loadMetadata(filePath string) (metadata, error) {
- cp, err := fileprovider.New().Retrieve(context.Background(), "file:"+filePath, nil)
- if err != nil {
- return metadata{}, err
- }
- conf, err := cp.AsConf()
- if err != nil {
- return metadata{}, err
- }
- md := metadata{}
- if err := conf.Unmarshal(&md); err != nil {
- return md, err
- }
- return md, nil
- }
- func run(folder string, allowlistFilePath string, generators []generator) error {
- components := map[string]metadata{}
- var foldersList []string
- maxLength := 0
- allCodeowners := map[string]struct{}{}
- err := filepath.Walk(folder, func(path string, info fs.FileInfo, err error) error {
- if info.Name() == "metadata.yaml" {
- m, err := loadMetadata(path)
- if err != nil {
- return err
- }
- if m.Status == nil {
- return nil
- }
- currentFolder := filepath.Dir(path)
- key := currentFolder
- components[key] = m
- foldersList = append(foldersList, key)
- for stability := range m.Status.Stability {
- if stability == unmaintainedStatus {
- // do not account for unmaintained status to change the max length of the component line.
- return nil
- }
- }
- if m.Status == nil || m.Status.Codeowners == nil {
- return fmt.Errorf("component %q has no status or codeowners section", key)
- }
- for _, id := range m.Status.Codeowners.Active {
- allCodeowners[id] = struct{}{}
- }
- if len(key) > maxLength {
- maxLength = len(key)
- }
- }
- return nil
- })
- if err != nil {
- return err
- }
- sort.Strings(foldersList)
- codeownersList := make([]string, 0, len(allCodeowners))
- for c := range allCodeowners {
- codeownersList = append(codeownersList, c)
- }
- sort.Strings(codeownersList)
- data := &githubData{
- folders: foldersList,
- codeowners: codeownersList,
- allowlistFilePath: allowlistFilePath,
- maxLength: maxLength,
- components: components,
- }
- for _, g := range generators {
- if err = g.generate(data); err != nil {
- return err
- }
- }
- return nil
- }
|