Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support gitlab sections #31

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 33 additions & 20 deletions cmd/codeowners/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,25 @@ import (
flag "github.com/spf13/pflag"
)

type Codeowners struct {
ownerFilters []string
showUnowned bool
codeownersPath string
helpFlag bool
sections bool
}

func main() {
var (
ownerFilters []string
showUnowned bool
codeownersPath string
helpFlag bool
c Codeowners
helpFlag bool
)
flag.StringSliceVarP(&ownerFilters, "owner", "o", nil, "filter results by owner")
flag.BoolVarP(&showUnowned, "unowned", "u", false, "only show unowned files (can be combined with -o)")
flag.StringVarP(&codeownersPath, "file", "f", "", "CODEOWNERS file path")

flag.StringSliceVarP(&c.ownerFilters, "owner", "o", nil, "filter results by owner")
flag.BoolVarP(&c.showUnowned, "unowned", "u", false, "only show unowned files (can be combined with -o)")
flag.StringVarP(&c.codeownersPath, "file", "f", "", "CODEOWNERS file path")
flag.BoolVarP(&helpFlag, "help", "h", false, "show this help message")
flag.BoolVarP(&c.sections, "sections", "", false, "support sections and inheritance")

flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: codeowners <path>...\n")
Expand All @@ -35,7 +43,7 @@ func main() {
os.Exit(0)
}

ruleset, err := loadCodeowners(codeownersPath)
ruleset, err := c.loadCodeowners()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
Expand All @@ -47,8 +55,8 @@ func main() {
}

// Make the @ optional for GitHub teams and usernames
for i := range ownerFilters {
ownerFilters[i] = strings.TrimLeft(ownerFilters[i], "@")
for i := range c.ownerFilters {
c.ownerFilters[i] = strings.TrimLeft(c.ownerFilters[i], "@")
}

out := bufio.NewWriter(os.Stdout)
Expand All @@ -57,7 +65,7 @@ func main() {
for _, startPath := range paths {
// godirwalk only accepts directories, so we need to handle files separately
if !isDir(startPath) {
if err := printFileOwners(out, ruleset, startPath, ownerFilters, showUnowned); err != nil {
if err := c.printFileOwners(out, ruleset, startPath); err != nil {
fmt.Fprintf(os.Stderr, "error: %v", err)
os.Exit(1)
}
Expand All @@ -71,7 +79,7 @@ func main() {

// Only show code owners for files, not directories
if !d.IsDir() {
return printFileOwners(out, ruleset, path, ownerFilters, showUnowned)
return c.printFileOwners(out, ruleset, path)
}
return nil
})
Expand All @@ -83,15 +91,15 @@ func main() {
}
}

func printFileOwners(out io.Writer, ruleset codeowners.Ruleset, path string, ownerFilters []string, showUnowned bool) error {
func (c Codeowners) printFileOwners(out io.Writer, ruleset codeowners.Ruleset, path 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 {
// Unless explicitly requested, don't show unowned files if we're filtering by owner
if len(ownerFilters) == 0 || showUnowned {
if len(c.ownerFilters) == 0 || c.showUnowned {
fmt.Fprintf(out, "%-70s (unowned)\n", path)
}
return nil
Expand All @@ -101,8 +109,8 @@ func printFileOwners(out io.Writer, ruleset codeowners.Ruleset, path string, own
ownersToShow := make([]string, 0, len(rule.Owners))
for _, o := range rule.Owners {
// If there are no filters, show all owners
filterMatch := len(ownerFilters) == 0 && !showUnowned
for _, filter := range ownerFilters {
filterMatch := len(c.ownerFilters) == 0 && !c.showUnowned
for _, filter := range c.ownerFilters {
if filter == o.Value {
filterMatch = true
}
Expand All @@ -119,11 +127,16 @@ func printFileOwners(out io.Writer, ruleset codeowners.Ruleset, path string, own
return nil
}

func loadCodeowners(path string) (codeowners.Ruleset, error) {
if path == "" {
return codeowners.LoadFileFromStandardLocation()
func (c Codeowners) loadCodeowners() (codeowners.Ruleset, error) {
var parseOptions []codeowners.ParseOption
if c.sections {
parseOptions = append(parseOptions, codeowners.WithSectionSupport())
}

if c.codeownersPath == "" {
return codeowners.LoadFileFromStandardLocation(parseOptions...)
}
return codeowners.LoadFile(path)
return codeowners.LoadFile(c.codeownersPath, parseOptions...)
}

// isDir checks if there's a directory at the path specified.
Expand Down
16 changes: 12 additions & 4 deletions codeowners.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,21 @@ import (
// LoadFileFromStandardLocation loads and parses a CODEOWNERS file at one of the
// standard locations for CODEOWNERS files (./, .github/, docs/). If run from a
// git repository, all paths are relative to the repository root.
func LoadFileFromStandardLocation() (Ruleset, error) {
func LoadFileFromStandardLocation(options ...ParseOption) (Ruleset, error) {
path := findFileAtStandardLocation()
if path == "" {
return nil, fmt.Errorf("could not find CODEOWNERS file at any of the standard locations")
}
return LoadFile(path)
return LoadFile(path, options...)
}

// LoadFile loads and parses a CODEOWNERS file at the path specified.
func LoadFile(path string) (Ruleset, error) {
func LoadFile(path string, options ...ParseOption) (Ruleset, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
return ParseFile(f)
return ParseFile(f, options...)
}

// findFileAtStandardLocation loops through the standard locations for
Expand Down Expand Up @@ -122,6 +122,14 @@ type Rule struct {
pattern pattern
}

type Section struct {
Name string
Owners []Owner
Comment string
ApprovalOptional bool
ApprovalCount int
}

// RawPattern returns the rule's gitignore-style path pattern.
func (r Rule) RawPattern() string {
return r.pattern.pattern
Expand Down
74 changes: 74 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,77 @@ func ExampleRuleset_Match() {
// src/foo/bar.go true
// src/foo.rs false
}

func ExampleRuleset_Match_section() {
f := bytes.NewBufferString(`[SECTION] @the-a-team
src
src-b @user-b
`)
ruleset, _ := codeowners.ParseFile(f, codeowners.WithSectionSupport())
match, _ := ruleset.Match("src")
fmt.Println("src", match != nil)
fmt.Println(ruleset[0].Owners[0].String())
match, _ = ruleset.Match("src-b")
fmt.Println("src-b", match != nil)
fmt.Println(ruleset[1].Owners[0].String())
// Output:
// src true
// @the-a-team
// src-b true
// @user-b
}

func ExampleRuleset_Match_section_groups() {
f := bytes.NewBufferString(`[SECTION] @the/a/group
src
src-b @user-b
src-c @the/c/group
`)
ruleset, _ := codeowners.ParseFile(f, codeowners.WithSectionSupport())
match, _ := ruleset.Match("src")
fmt.Println("src", match != nil)
fmt.Println(ruleset[0].Owners[0].String())
match, _ = ruleset.Match("src-b")
fmt.Println("src-b", match != nil)
fmt.Println(ruleset[1].Owners[0].String())
match, _ = ruleset.Match("src-c")
fmt.Println("src-c", match != nil)
fmt.Println(ruleset[2].Owners[0].String())
// Output:
// src true
// @the/a/group
// src-b true
// @user-b
// src-c true
// @the/c/group
}

func ExampleRuleset_Match_section_groups_multiple() {
f := bytes.NewBufferString(`[SECTION] @the/a/group
* @other

[SECTION-B] @the/b/group
b-src
b-src-b @user-b
b-src-c @the/c/group

[SECTION-C]
`)
ruleset, _ := codeowners.ParseFile(f, codeowners.WithSectionSupport())
match, _ := ruleset.Match("b-src")
fmt.Println("b-src", match != nil)
fmt.Println(ruleset[1].Owners[0].String())
match, _ = ruleset.Match("b-src-b")
fmt.Println("b-src-b", match != nil)
fmt.Println(ruleset[2].Owners[0].String())
match, _ = ruleset.Match("b-src-c")
fmt.Println("b-src-c", match != nil)
fmt.Println(ruleset[3].Owners[0].String())
// Output:
// b-src true
// @the/b/group
// b-src-b true
// @user-b
// b-src-c true
// @the/c/group
}
Loading