1 package owners
2
3 import (
4 "context"
5 "flag"
6 "fmt"
7 "os"
8 "path/filepath"
9
10 "github.com/peterbourgon/ff/v3"
11 "github.com/peterbourgon/ff/v3/ffcli"
12 "gopkg.in/yaml.v2"
13
14 repoowners "edge-infra.dev/pkg/f8n/devinfra/repo/owners"
15 )
16
17 const (
18 pkg = "pkg/"
19 cmd = "cmd/"
20 pallets = "config/pallets/"
21 )
22
23 type arrayFlags []string
24
25 func (i *arrayFlags) String() string {
26 return "list of strings"
27 }
28
29 func (i *arrayFlags) Set(value string) error {
30 *i = append(*i, value)
31 return nil
32 }
33
34 type create struct {
35 *owners
36
37 teams arrayFlags
38 usernames arrayFlags
39 dirs arrayFlags
40 pkg bool
41 cmd bool
42 pallets bool
43 }
44
45
46 func newCreate(o *owners) *ffcli.Command {
47 c := &create{owners: o}
48
49 fs := flag.NewFlagSet("hack owners create", flag.ExitOnError)
50 c.owners.RegisterFlags(fs)
51
52 fs.Var(&c.teams, "team", "list of teams to add")
53 fs.Var(&c.usernames, "username", "list of usernames to add")
54 fs.Var(&c.dirs, "dir", "list of directories to create a new OWNERS file")
55
56 fs.BoolVar(&c.pkg, "pkg", false, "create the /pkg dir")
57 fs.BoolVar(&c.cmd, "cmd", false, "create the /cmd dir")
58 fs.BoolVar(&c.pallets, "pallets", false, "create the config/pallets dir")
59
60 return &ffcli.Command{
61 Name: "create",
62 FlagSet: fs,
63 Exec: c.Exec,
64 Options: []ff.Option{
65 ff.WithEnvVarNoPrefix(),
66 },
67 }
68 }
69
70 func (c *create) Exec(_ context.Context, _ []string) error {
71
72 if len(c.teams) == 0 && len(c.usernames) == 0 {
73 return fmt.Errorf("a team or user must be defined")
74 }
75
76
77 if len(c.dirs) == 0 {
78 return fmt.Errorf("a directory must be defined")
79 }
80
81
82 _, ownersFiles, err := c.collectOwners()
83 if err != nil {
84 return fmt.Errorf("failed to collect OWNERS files: %w", err)
85 }
86
87
88 if !c.pkg && !c.cmd && !c.pallets {
89 for _, dir := range c.dirs {
90 c.generateFile(dir, ownersFiles)
91 }
92 return c.update()
93 }
94
95
96 dir := c.dirs[0]
97 if len(c.dirs) > 1 {
98 fmt.Printf("⚠️ more than one dir was given but only %s will be used \n", dir)
99 fmt.Println("⚠️ drop the pkg, cmd, or pallet flag for all dirs to be created")
100 }
101
102 if c.pkg {
103 c.generateFile(filepath.Join(pkg, dir), ownersFiles)
104 }
105
106 if c.cmd {
107 c.generateFile(filepath.Join(cmd, dir), ownersFiles)
108 }
109
110 if c.pallets {
111 c.generateFile(filepath.Join(pallets, dir), ownersFiles)
112 }
113 return c.update()
114 }
115
116
117 func (c *create) update() error {
118 u := &update{owners: c.owners}
119 d, err := u.generatePBotCfg()
120 if err != nil {
121 return err
122 }
123 return u.createFile(d)
124 }
125
126 func (c *create) generateFile(dir string, ownersFiles map[string]repoowners.File) {
127
128
129 if _, ok := ownersFiles[dir]; !ok {
130 f := repoowners.DefaultFile(c.usernames, c.teams, dir)
131 fp := filepath.Join(c.Paths.RepoRoot, dir, repoowners.FileName)
132 fmt.Printf("✅ %s created \n", fp)
133 err := createPathAndFile(f, fp)
134 if err != nil {
135 fmt.Printf("failed to create file and path: %v", err)
136 }
137 } else {
138 fmt.Printf("〰️ %s already exists ignoring \n", dir)
139 }
140 }
141
142 func createPathAndFile(file *repoowners.File, path string) error {
143 if err := os.MkdirAll(filepath.Dir(path), 0770); err != nil {
144 return fmt.Errorf("Error creating dirs: %w", err)
145 }
146 _, err := os.Create(path)
147 if err != nil {
148 return fmt.Errorf("could not create file path: %w", err)
149 }
150
151 yamlData, err := yaml.Marshal(&file)
152 if err != nil {
153 return fmt.Errorf("Error while Marshaling: %w", err)
154 }
155
156 err = os.WriteFile(path, yamlData, 0644)
157 if err != nil {
158 return fmt.Errorf("unable to write data into the file: %w", err)
159 }
160 return nil
161 }
162
View as plain text