forked from hmarr/codeowners
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
133 lines (116 loc) Β· 3.02 KB
/
main.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/dicksontung/codeowners"
"github.com/karrick/godirwalk"
flag "github.com/spf13/pflag"
)
func main() {
var (
ownerFilters []string
codeownersPath string
helpFlag bool
)
flag.StringSliceVarP(&ownerFilters, "owner", "o", nil, "filter results by owner")
flag.StringVarP(&codeownersPath, "file", "f", "", "CODEOWNERS file path")
flag.BoolVarP(&helpFlag, "help", "h", false, "show this help message")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: codeowners <path>...\n")
flag.PrintDefaults()
}
flag.Parse()
if helpFlag {
flag.Usage()
os.Exit(0)
}
ruleset, err := loadCodeowners(codeownersPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
paths := flag.Args()
if len(paths) == 0 {
paths = append(paths, ".")
}
// Make the @ optional for GitHub teams and usernames
for i := range ownerFilters {
ownerFilters[i] = strings.TrimLeft(ownerFilters[i], "@")
}
for _, startPath := range paths {
// godirwalk only accepts directories, so we need to handle files separately
if !isDir(startPath) {
if err := printFileOwners(ruleset, startPath, ownerFilters); err != nil {
fmt.Fprintf(os.Stderr, "error: %v", err)
os.Exit(1)
}
continue
}
err = godirwalk.Walk(startPath, &godirwalk.Options{
Callback: func(path string, dirent *godirwalk.Dirent) error {
if path == ".git" {
return filepath.SkipDir
}
// Only show code owners for files, not directories
if !dirent.IsDir() {
return printFileOwners(ruleset, path, ownerFilters)
}
return nil
},
Unsorted: true,
})
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v", err)
os.Exit(1)
}
}
}
func printFileOwners(ruleset codeowners.Ruleset, path string, ownerFilters []string) error {
rule, err := ruleset.Match(path)
if err != nil {
return err
}
// If we didn't get a match, the file is unowned
if rule == nil || rule.Owners == nil {
// Don't show unowned files if we're filtering by owner
if len(ownerFilters) == 0 {
fmt.Printf("%-70s (unowned)\n", path)
}
return nil
}
// Figure out which of the owners we need to show according to the --owner filters
owners := []string{}
for _, o := range rule.Owners {
// If there are no filters, show all owners
filterMatch := len(ownerFilters) == 0
for _, filter := range ownerFilters {
if filter == o.Value {
filterMatch = true
}
}
if filterMatch {
owners = append(owners, o.String())
}
}
// If the owners slice is empty, no owners matched the filters so don't show anything
if len(owners) > 0 {
fmt.Printf("%-70s %s\n", path, strings.Join(owners, " "))
}
return nil
}
func loadCodeowners(path string) (codeowners.Ruleset, error) {
if path == "" {
return codeowners.LoadFileFromStandardLocation()
}
return codeowners.LoadFile(path)
}
// isDir checks if there's a directory at the path specified.
func isDir(path string) bool {
info, err := os.Stat(path)
if os.IsNotExist(err) {
return false
}
return info.IsDir()
}