codeowners

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 26, 2024 License: MIT Imports: 10 Imported by: 7

README

codeowners

build PkgGoDev

A CLI and Go library for GitHub's CODEOWNERS file.

Command line tool

The codeowners CLI identifies the owners for files in a local repository or directory.

Installation

If you're on macOS, you can install the CLI from the homebrew tap.

$ brew tap hmarr/tap
$ brew install codeowners

Otherwise, grab a binary from the releases page or install from source with go install:

$ go install github.com/hmarr/codeowners/cmd/codeowners@latest
Usage

By default, the command line tool will walk the directory tree, printing the code owners of any files that are found.

$ codeowners --help
usage: codeowners <path>...
  -f, --file string     CODEOWNERS file path
  -h, --help            show this help message
  -o, --owner strings   filter results by owner
  -u, --unowned         only show unowned files (can be combined with -o)

$ ls
CODEOWNERS       DOCUMENTATION.md README.md        example.go       example_test.go

$ cat CODEOWNERS
*.go       @example/go-engineers
*.md       @example/docs-writers
README.md  product-manager@example.com

$ codeowners
CODEOWNERS                           (unowned)
README.md                            product-manager@example.com
example_test.go                      @example/go-engineers
example.go                           @example/go-engineers
DOCUMENTATION.md                     @example/docs-writers

To limit the files the tool looks at, provide one or more paths as arguments.

$ codeowners *.md
README.md                            product-manager@example.com
DOCUMENTATION.md                     @example/docs-writers

Pass the --owner flag to filter results by a specific owner.

$ codeowners -o @example/go-engineers
example_test.go                      @example/go-engineers
example.go                           @example/go-engineers

Pass the --unowned flag to only show unowned files.

$ codeowners -u
CODEOWNERS                           (unowned)

Go library

A package for parsing CODEOWNERS files and matching files to owners.

Installation
$ go get github.com/hmarr/codeowners
Usage

Full documentation is available at pkg.go.dev.

Here's a quick example to get you started:

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/hmarr/codeowners"
)

func main() {
	file, err := os.Open("CODEOWNERS")
	if err != nil {
		log.Fatal(err)
	}

	ruleset, err := codeowners.ParseFile(file)
	if err != nil {
		log.Fatal(err)
	}

	rule, err := ruleset.Match("path/to/file")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Owners: %v\n", rule.Owners)
}

Documentation

Overview

Package codeowners is a library for working with CODEOWNERS files.

CODEOWNERS files map gitignore-style path patterns to sets of owners, which may be GitHub users, GitHub teams, or email addresses. This library parses the CODEOWNERS file format into rulesets, which may then be used to determine the ownership of files.

Usage

To find the owner of a given file, parse a CODEOWNERS file and call Match() on the resulting ruleset.

ruleset, err := codeowners.ParseFile(file)
if err != nil {
	log.Fatal(err)
}

rule, err := ruleset.Match("path/to/file")
if err != nil {
	log.Fatal(err)
}

Command line interface

A command line interface is also available in the cmd/codeowners package. When run, it will walk the directory tree showing the code owners for each file encountered. The help flag lists available options.

$ codeowners --help
Example
package main

import (
	"bytes"
	"fmt"

	"github.com/hmarr/codeowners"
)

func main() {
	f := bytes.NewBufferString("src/**/*.c @acme/c-developers")
	ruleset, err := codeowners.ParseFile(f)
	if err != nil {
		panic(err)
	}

	match, err := ruleset.Match("src/foo.c")
	fmt.Println(match.Owners)

	match, err = ruleset.Match("src/foo.rs")
	fmt.Println(match)
}
Output:

[@acme/c-developers]
<nil>

Index

Examples

Constants

View Source
const (
	// EmailOwner is the owner type for email addresses.
	EmailOwner string = "email"
	// TeamOwner is the owner type for GitHub teams.
	TeamOwner string = "team"
	// UsernameOwner is the owner type for GitHub usernames.
	UsernameOwner string = "username"
)

Variables

DefaultOwnerMatchers is the default set of owner matchers, which includes the GitHub-flavored email, team, and username matchers.

View Source
var ErrNoMatch = errors.New("no match")

Functions

func WithOwnerMatchers added in v1.2.0

func WithOwnerMatchers(mm []OwnerMatcher) parseOption

Types

type ErrInvalidOwnerFormat added in v1.2.0

type ErrInvalidOwnerFormat struct {
	Owner string
}

func (ErrInvalidOwnerFormat) Error added in v1.2.0

func (err ErrInvalidOwnerFormat) Error() string

type Owner

type Owner struct {
	// Value is the name of the owner: the email addres, team name, or username.
	Value string
	// Type will be one of 'email', 'team', or 'username'.
	Type string
}

Owner represents an owner found in a rule.

func MatchEmailOwner added in v1.2.0

func MatchEmailOwner(s string) (Owner, error)

MatchEmailOwner matches an email address owner. May be provided to WithOwnerMatchers.

func MatchTeamOwner added in v1.2.0

func MatchTeamOwner(s string) (Owner, error)

MatchTeamOwner matches a GitHub team owner. May be provided to WithOwnerMatchers.

func MatchUsernameOwner added in v1.2.0

func MatchUsernameOwner(s string) (Owner, error)

MatchUsernameOwner matches a GitHub username owner. May be provided to WithOwnerMatchers.

func (Owner) String

func (o Owner) String() string

String returns a string representation of the owner. For email owners, it simply returns the email address. For user and team owners it prepends an '@' to the owner.

type OwnerMatchFunc added in v1.2.0

type OwnerMatchFunc func(s string) (Owner, error)

OwnerMatchFunc is a function that matches a string against a pattern and returns an Owner, or ErrNoMatch if no match was found. It implements the OwnerMatcher interface and may be provided to WithOwnerMatchers to customize owner matching behavior (e.g. to support GitLab-style team names).

func (OwnerMatchFunc) Match added in v1.2.0

func (f OwnerMatchFunc) Match(s string) (Owner, error)

type OwnerMatcher added in v1.2.0

type OwnerMatcher interface {
	// Matches give string agains a pattern e.g. a regexp.
	// Should return ErrNoMatch if the pattern doesn't match.
	Match(s string) (Owner, error)
}

type Rule

type Rule struct {
	Owners     []Owner
	Comment    string
	LineNumber int
	// contains filtered or unexported fields
}

Rule is a CODEOWNERS rule that maps a gitignore-style path pattern to a set of owners.

func (Rule) Match

func (r Rule) Match(path string) (bool, error)

Match tests whether the provided matches the rule's pattern.

func (Rule) RawPattern

func (r Rule) RawPattern() string

RawPattern returns the rule's gitignore-style path pattern.

type Ruleset

type Ruleset []Rule

Ruleset is a collection of CODEOWNERS rules.

func LoadFile added in v0.2.0

func LoadFile(path string) (Ruleset, error)

LoadFile loads and parses a CODEOWNERS file at the path specified.

func LoadFileFromStandardLocation added in v0.2.0

func LoadFileFromStandardLocation() (Ruleset, error)

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 ParseFile

func ParseFile(f io.Reader, options ...parseOption) (Ruleset, error)

ParseFile parses a CODEOWNERS file, returning a set of rules. To override the default owner matchers, pass WithOwnerMatchers() as an option.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/hmarr/codeowners"
)

func main() {
	f := bytes.NewBufferString("src/**/*.go @acme/go-developers # Go code")
	ruleset, err := codeowners.ParseFile(f)
	if err != nil {
		panic(err)
	}
	fmt.Println(len(ruleset))
	fmt.Println(ruleset[0].RawPattern())
	fmt.Println(ruleset[0].Owners[0].String())
	fmt.Println(ruleset[0].Comment)
}
Output:

1
src/**/*.go
@acme/go-developers
Go code
Example (CustomOwnerMatchers)
package main

import (
	"bytes"
	"fmt"
	"regexp"

	"github.com/hmarr/codeowners"
)

func main() {
	validUsernames := []string{"the-a-team", "the-b-team"}
	usernameRegexp := regexp.MustCompile(`\A@([a-zA-Z0-9\-]+)\z`)

	f := bytes.NewBufferString("src/**/*.go @the-a-team # Go code")
	ownerMatchers := []codeowners.OwnerMatcher{
		codeowners.OwnerMatchFunc(codeowners.MatchEmailOwner),
		codeowners.OwnerMatchFunc(func(s string) (codeowners.Owner, error) {
			// Custom owner matcher that only matches valid usernames
			match := usernameRegexp.FindStringSubmatch(s)
			if match == nil {
				return codeowners.Owner{}, codeowners.ErrNoMatch
			}

			for _, t := range validUsernames {
				if t == match[1] {
					return codeowners.Owner{Value: match[1], Type: codeowners.TeamOwner}, nil
				}
			}
			return codeowners.Owner{}, codeowners.ErrNoMatch
		}),
	}
	ruleset, err := codeowners.ParseFile(f, codeowners.WithOwnerMatchers(ownerMatchers))
	if err != nil {
		panic(err)
	}
	fmt.Println(len(ruleset))
	fmt.Println(ruleset[0].RawPattern())
	fmt.Println(ruleset[0].Owners[0].String())
	fmt.Println(ruleset[0].Comment)
}
Output:

1
src/**/*.go
@the-a-team
Go code

func (Ruleset) Match

func (r Ruleset) Match(path string) (*Rule, error)

Match finds the last rule in the ruleset that matches the path provided. When determining the ownership of a file using CODEOWNERS, order matters, and the last matching rule takes precedence.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/hmarr/codeowners"
)

func main() {
	f := bytes.NewBufferString("src/**/*.go @acme/go-developers # Go code")
	ruleset, _ := codeowners.ParseFile(f)

	match, _ := ruleset.Match("src")
	fmt.Println("src", match != nil)

	match, _ = ruleset.Match("src/foo.go")
	fmt.Println("src/foo.go", match != nil)

	match, _ = ruleset.Match("src/foo/bar.go")
	fmt.Println("src/foo/bar.go", match != nil)

	match, _ = ruleset.Match("src/foo.rs")
	fmt.Println("src/foo.rs", match != nil)
}
Output:

src false
src/foo.go true
src/foo/bar.go true
src/foo.rs false

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL