Skip to main content

Crate codeowner

Crate codeowner 

Source
Expand description

Parse GitHub CODEOWNERS files and answer the only question that matters: who owns this file?

use codeowner::{CodeOwners, Owner};

let owners = CodeOwners::parse("\
*                @org/everyone
/src/parser/     @alice
*.md             docs@example.com
");

assert_eq!(owners.of("src/parser/lexer.rs"), Some(&[Owner::user("alice")][..]));
assert_eq!(owners.of("README.md"),           Some(&[Owner::email("docs@example.com")][..]));
assert_eq!(owners.of("build.sh"),            Some(&[Owner::team("org", "everyone")][..]));

§Three things implementations usually get wrong

1. Unowned is not the same as unmatched. A rule with no owners deliberately clears ownership. GitHub documents this. Collapsing the two cases into one silently reassigns files to the wrong team.

use codeowner::CodeOwners;

let owners = CodeOwners::parse("/apps/ @octocat\n/apps/github\n");

assert!(owners.of("apps/main/index.js").is_some()); // owned by @octocat
assert_eq!(owners.of("apps/github/index.js").map(<[_]>::len), Some(0)); // matched, no owner
assert_eq!(owners.of("README.md"), None);           // no rule matched at all

2. CODEOWNERS is not gitignore. GitHub documents three gitignore features as non-functional here: ! negation, [ ] character ranges, and \ escaping of a leading #. Lines using them are invalid and skipped — this crate reports them rather than silently mis-parsing.

3. docs/* does not match nested files. Under gitignore rules it would, by matching the intermediate directory. GitHub says it does not.

use codeowner::CodeOwners;

let owners = CodeOwners::parse("docs/* docs@example.com\n");
assert!(owners.of("docs/getting-started.md").is_some());
assert!(owners.of("docs/build-app/troubleshooting.md").is_none());

§Errors are data, not failures

GitHub skips invalid lines rather than rejecting the file, so CodeOwners::parse never fails. Bad lines land in errors with line numbers, which is what you want if you are writing a linter.

use codeowner::CodeOwners;

let owners = CodeOwners::parse("*.rs @alice\n![!]bad @bob\n");
assert_eq!(owners.rules().len(), 1);
assert_eq!(owners.errors().len(), 1);
assert_eq!(owners.errors()[0].line, 2);

§Scope

GitHub CODEOWNERS syntax, zero dependencies, no_std (needs alloc). GitLab’s section headers ([Backend][2]) are not supported yet.

Structs§

CodeOwners
A parsed CODEOWNERS file.
ParseError
A skipped line, with enough context to point at it in an editor.
Pattern
A compiled CODEOWNERS path pattern.
Rule
One pattern owners... line.

Enums§

ErrorKind
What was wrong with a line.
Owner
A single code owner.
PatternError
Why a pattern could not be compiled.

Constants§

MAX_FILE_SIZE
GitHub refuses to load a CODEOWNERS file above this size.
SEARCH_PATHS
The paths GitHub searches, in priority order.