Skip to content

g1t/packages/contracts/src/codeowners.ts

94 lines2,682 bytesCodeBlame
1/**
2 * CODEOWNERS: who owns which paths of a repository, read from the branch a
3 * pull request merges into. Mirrors `crates/contracts/src/codeowners.rs`,
4 * which holds the parser and the rules.
5 */
6
7/** Where the file is looked for, first found wins. */
8export const CODEOWNERS_LOCATIONS = [
9 ".g1t/CODEOWNERS",
10 ".github/CODEOWNERS",
11 "CODEOWNERS",
12 "docs/CODEOWNERS",
13 ".gitlab/CODEOWNERS",
14] as const;
15
16/** Whether `path` is one of the places a CODEOWNERS file is read from. */
17export function isCodeownersPath(path: string): boolean {
18 return (CODEOWNERS_LOCATIONS as readonly string[]).includes(path);
19}
20
21export type CodeownersErrorKind =
22 | "too_large"
23 | "negation"
24 | "character_range"
25 | "bad_pattern"
26 | "bad_owner"
27 | "bad_section"
28 | "unknown_user"
29 | "unknown_team"
30 | "unknown_email"
31 | "no_write_access"
32 | "team_no_access";
33
34/** One problem with a CODEOWNERS file, on a line (0 for the whole file). */
35export type CodeownersError = {
36 line: number;
37 kind: CodeownersErrorKind;
38 /** The owner or pattern at fault, as written. */
39 token: string | null;
40 message: string;
41};
42
43/** A CODEOWNERS file at one branch, checked like a linter. */
44export type CodeownersReport = {
45 /** Where it was found; null when there is none. */
46 path: string | null;
47 ref: string;
48 size: number;
49 rules: number;
50 sections: string[];
51 errors: CodeownersError[];
52};
53
54/** Where one rule's review stands on a pull request. */
55export type OwnerReview = {
56 /** The section's name; null for rules before any section. */
57 section: string | null;
58 line: number;
59 pattern: string;
60 /** Owners as written: `@ana`, `@acme/backend`, `ana@example.com`. */
61 owners: string[];
62 files: string[];
63 optional: boolean;
64 required: number;
65 approved_by: string[];
66 changes_requested_by: string[];
67 satisfied: boolean;
68};
69
70/** Who owns what a pull request changes, and whose approval is still needed. */
71export type PullCodeOwners = {
72 path: string;
73 /** Whether branch protection requires code owners' approval. */
74 required: boolean;
75 reviews: OwnerReview[];
76 /** What still stands in the way; null when nothing does. */
77 missing: string | null;
78 errors: number;
79};
80
81/** A short label for an error kind, for the errors view. */
82export const CODEOWNERS_ERROR_LABELS: Record<CodeownersErrorKind, string> = {
83 too_large: "Too large",
84 negation: "Negation",
85 character_range: "Character range",
86 bad_pattern: "Bad pattern",
87 bad_owner: "Bad owner",
88 bad_section: "Bad section",
89 unknown_user: "Unknown user",
90 unknown_team: "Unknown team",
91 unknown_email: "Unknown email",
92 no_write_access: "No write access",
93 team_no_access: "Team without access",
94};