Git, CI/CD & GitOpsXXXI · CODEOWNERS and Ownership ControlsSyntax
Syntax and patterns — the line format, glob rules, and the order trap
What you'll learn
- Write a CODEOWNERS line that pins an owner to a single file versus a directory tree
- Predict which lines match a given path and which one wins on conflict
- Distinguish leading-slash-anchored patterns from unanchored glob patterns
- Identify the four common syntax mistakes that turn CODEOWNERS into documentation
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A CODEOWNERS file is a sequence of lines, most of which pair a pattern with one or more owners. The format is deliberately small: anything you can do in CODEOWNERS, you can do in a few dozen lines. What makes the format hard to get right is the order rule and the semantics of leading slashes.
The line format
Each line is <pattern> <owner1> [<owner2> ...]. Comments start
with #. Blank lines are ignored. On GitHub an optional
[Section] header in brackets starts a new section but has no
semantic effect.
# Default owners for everything in the repo
* @platform/platform-infra
/terraform/ @platform/platform-infra
/terraform/prod/ @platform/platform-infra @security-team
/prod/secrets.yaml @security-team
A line with only a pattern and no owner is invalid; the forge will report it on push. A line that references a user or team that does not exist will silently fail to assign that owner - which is one of the ways CODEOWNERS becomes documentation.
Pattern semantics
Three rules govern matching:
- A pattern without a leading slash matches anywhere in the
repo.
*.tfmatchesmain.tfat the root and alsomodules/vpc/main.tfdeep in the tree. - A pattern with a leading slash is anchored at the repo
root.
/main.tfmatches only the filemain.tfat the root;modules/vpc/main.tfis not matched by/main.tf. - A pattern ending with
/matches directories recursively./terraform/matches every file under/terraform/.
Glob characters are * (single path segment), ** (any
sequence of segments), ? (single character), and [abc]
(character class).
cat .github/CODEOWNERS
A worked example, given terraform/prod/iam/main.tf:
*.tf @platform/platform-infra
/terraform/ @platform/platform-infra
/terraform/prod/ @platform/platform-infra @security-team
main.tf @platform/platform-infra
The file is matched by the first three lines and not by the fourth. All matching owners accumulate.
The order rule
When two lines match the same path, the last matching line wins. This is the rule that produces the most subtle bugs.
/terraform/ @platform/platform-infra
/terraform/prod/ @security-team
For a file under /terraform/prod/, both lines match; the
second wins, so the owners are @security-team. For a file under
/terraform/staging/, only the first line matches, so the owner
is @platform/platform-infra.
flowchart LR
A[Path under review] --> B[Walk CODEOWNERS top to bottom]
B --> C{Line matches?}
C -->|yes| D[Add owners to running set]
D --> E{More lines?}
E -->|yes| B
E -->|no| F[Final set of owners]
C -->|no| E
The “last match wins” rule applies to the primary assignment in GitLab semantics; on GitHub all matching lines accumulate owners. The semantic differences between forges matter and are revisited in the next lesson.
Validating the file
There is no canonical codeowners-lint, but every forge reports
syntax errors on push. The practical validation is to open a PR
that touches one path per rule, confirm the right reviewers are
assigned, and confirm the merge is blocked until they approve.
git ls-files | grep CODEOWNERS
Production discipline
Treat CODEOWNERS changes as code changes: open a PR, get two reviewers, run a test PR through the forge, and confirm the reviewer list matches the expectation. A CODEOWNERS rule that has never been exercised against a real PR is a rule that has not been validated.
Cross-course references
- Ansible for Production Sysadmins - Part XXXIX (InventoryPatterns) covers glob matching against inventory; CODEOWNERS globs are the same shape.
Quiz
Knowledge check · 4 questions
Q1. A file at /terraform/prod/iam/main.tf is reviewed against the example CODEOWNERS. On GitHub, which owners are assigned?
Q2. The CODEOWNERS pattern /terraform/ matches the file terraform/prod/main.tf.
Q3. Explain the difference between terraform/*.tf and terraform/**/*.tf in CODEOWNERS.
Q4. Diagnose why a CODEOWNERS rule is matching files the team did not intend to cover.
A team writes '*.tf @platform/platform-infra' to assign every Terraform file at the repo root to platform. After deploying, they find that modules/vpc/main.tf - deep in the tree - is also assigned to platform. They wanted only root-level .tf files covered.
Passing score: 75%. Answers are checked in this browser.