Each CODEOWNERS rule names a workable number of owners
gitlab/codeowners-owners-per-rule@v1
Every rule in CODEOWNERS ends up with between the minimum and maximum number of owners, counting the default owners it inherits from its section header, so review depends neither on a single person nor on everyone.
| Id | gitlab/codeowners-owners-per-rule |
| Version | v1 |
| Category | gitlab |
| Default severity | warning |
| Interpreter | python3 |
| Timeout | 30 seconds |
| Violations tolerated | 0 |
| Collects | gitlab |
Collectors
This guardrail doesn't gather anything itself. It relies on the collectors below, which the CLI runs once per build before any check, and reads what they found from GUARDRAIL_FACTS. If a collector collects nothing, this guardrail is skipped, not failed.
| Collector | Gathers | Inputs it is given |
|---|---|---|
gitlab | What GitLab itself reads out of the repository: the .gitlab-ci.yml, read as GitLab writes it, and the CODEOWNERS file, read as sections of rules. For the pipeline, the stages it declares, what it includes, and for every job the stage it sits in, the image and runner tags it asks for, how long it may run, the rules that decide whether it runs at all, and each script section with the variables it interpolates. For CODEOWNERS, every section with whether it is optional and how many approvals it needs, the rules under each, and the owners that apply to the paths a guardrail asks about, resolved per section the way GitLab resolves them. Names and shapes only, never a secret, a variable value or an environment value. | pipelines |
The inputs above are this guardrail's own inputs, passed straight through to the collector. Setting one in buildnote.json changes what is collected, and two guardrails configured the same way share a single collection.
Configuration
{
"guardrails": {
"failOn": "error",
"checks": [
{
"use": "gitlab/codeowners-owners-per-rule@v1",
"severity": "warning",
"with": {
"pipelines": ".gitlab-ci.yml,.gitlab-ci.yaml",
"minOwners": "1",
"maxOwners": "5"
},
"exemptions": []
}
]
}
}Inputs
| Input | Description | Default | Environment variable |
|---|---|---|---|
pipelines | Comma separated globs naming the GitLab CI files to read. | .gitlab-ci.yml,.gitlab-ci.yaml | GUARDRAIL_INPUT_PIPELINES |
minOwners | Fewest owners a rule may end up with. A rule that ends up with none is covered by gitlab/codeowners-no-unowned-rules. | 1 | GUARDRAIL_INPUT_MINOWNERS |
maxOwners | Most owners a rule may end up with. 0 means no ceiling. | 5 | GUARDRAIL_INPUT_MAXOWNERS |
How to fix
Adjust the rules named in the violations, or the section header they inherit their owners from. A single owner is a single point of failure: while they are on leave, nobody can approve changes to the path. A long list fails the other way, because GitLab asks all of them and each one assumes someone else will pick it up.
A group owner counts as one:
[Backend][2] @company/backend
/api/This is usually the right answer. Membership then lives in the group instead of in this file, and the section's approval count sets how many members have to approve.
More in gitlab
gitlab/codeowners-catch-all. A section that requires approval declares a rule matching every path, so a file nobody thought about still has an owner who must approve it.gitlab/codeowners-no-unowned-rules. No rule inCODEOWNERSends up with no owners, counting the default owners it inherits from its section header, so no section claims a path without naming someone for it.gitlab/codeowners-parses. Every line ofCODEOWNERSthat isn't a comment is either a section header or a rule, so no ownership is silently lost to a line GitLab ignores.gitlab/codeowners-present. The project has aCODEOWNERSfile in one of the three places GitLab reads it from, and at least one section of it declares a rule, so every change has someone to review it.gitlab/codeowners-team-owned. Every rule inCODEOWNERSends up owned by at least one GitLab group, so ownership survives changes in who is on the team.gitlab/job-timeout-set. Every job declares how long it may run, so a hung job is cut off rather than holding a runner until the project's own limit.