-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile.go
111 lines (90 loc) · 3.01 KB
/
file.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Package gitlabcodeowners provides parsing and querying
// function to work with `CODEOWNERS` file from Gitlab.
// See https://docs.gitlab.com/ee/user/project/codeowners
// for more details.
package gitlabcodeowners
import (
"io"
)
// File is a representation of a parsed `CODEOWNERS` file.
type File struct {
sections []section
}
// Approval describes an approval required by a rule in the `CODEOWNERS` file.
type Approval struct {
Pattern string
Approvals int
Owners []string
}
// GetPossibleCodeOwnersLocations returns a list of possible locations
// where a `CODEOWNERS` file can be located according to Gitlab.
func GetPossibleCodeOwnersLocations() []string {
return []string{"/CODEOWNERS", "/docs/CODEOWNERS", "/.gitlab/CODEOWNERS"}
}
// NewCodeOwnersFile tries to parse the given description and returns a `File`
// instance if parsing succeeded otherwise it return an error.
func NewCodeOwnersFile(reader io.Reader) (File, error) {
sections, err := parseFile(reader)
if err != nil {
return File{}, err
}
return File{sections: sections}, nil
}
// GetRequiredApprovalsForFile returns a map of all approvals which
// apply to the file given by it's path. All path need to start with
// a `/` which represents the root folder of the repository.
func (f File) GetRequiredApprovalsForFile(path string) map[string]Approval {
requiredApprovals := map[string]Approval{}
for _, sec := range f.sections {
found := false
rule := rule{} //nolint:exhaustruct // used as placeholder if no rule is found
for _, r := range sec.rules {
if isValidRule(r, sec.owners) && r.pattern.match(path) {
rule = r
found = true
}
}
if found {
owners := sec.owners
if len(rule.owners) > 0 {
owners = rule.owners
}
requiredApprovals[sec.name] = Approval{
Pattern: rule.pattern.value,
Approvals: sec.approvals,
Owners: owners,
}
}
}
return requiredApprovals
}
// GetRequiredApprovalsForFiles returns a map of all approvals which
// apply to the files given by their path. All paths need to start with
// a `/` which represents the root folder of the repository.
func (f File) GetRequiredApprovalsForFiles(paths []string) map[string][]Approval {
requiredApprovals := map[string][]Approval{}
for _, path := range paths {
for section, approval := range f.GetRequiredApprovalsForFile(path) {
existingApprovals := requiredApprovals[section]
requiredApprovals[section] = append(existingApprovals, approval)
}
}
for section, approvals := range requiredApprovals {
requiredApprovals[section] = removeDuplicatedApprovals(approvals)
}
return requiredApprovals
}
func isValidRule(rule rule, defaultOwners []string) bool {
return (len(rule.owners) + len(defaultOwners)) > 0
}
func removeDuplicatedApprovals(approvals []Approval) []Approval {
result := []Approval{}
patterns := map[string]bool{}
for _, approval := range approvals {
if _, existing := patterns[approval.Pattern]; !existing {
patterns[approval.Pattern] = true
result = append(result, approval)
}
}
return result
}