Code Owners syntax and error handling (PREMIUM ALL)
This page describes the syntax and error handling used in Code Owners files, and provides an example file.
Code Owners syntax
Comments
Lines beginning with #
are ignored:
# This is a comment
Sections
Sections are groups of entries. A section begins with a section heading in square brackets, followed by the entries.
[Section name]
/path/of/protected/file.rb @username
/path/of/protected/dir/ @group
Section headings
Section headings must always have a name. They can also be made optional, or require a number of approvals. A list of default owners can be added to the section heading line.
# Required section
[Section name]
# Optional section
^[Section name]
# Section requiring 5 approvals
[Section name][5]
# Section with @username as default owner
[Section name] @username
# Section with @group and @subgroup as default owners and requiring 2 approvals
[Section name][2] @group @subgroup
Section names
Sections names are defined between square brackets. Section names are not case-sensitive. Sections with duplicate names are combined.
[Section name]
Required sections
Required sections do not include ^
before the section name.
[Required section]
Optional sections
Optional sections include a ^
before the section name.
^[Optional section]
Sections requiring multiple approvals
Sections requiring multiple approvals include the number of approvals in square brackets after the section name.
[Section requiring 5 approvals][5]
NOTE: Optional sections ignore the number of approvals required.
Sections with default owners
You can define a default owner for the entries in a section by appending the owners to the section heading.
# Section with @username as default owner
[Section name] @username
# Section with @group and @subgroup as default owners and requiring 2 approvals
[Section name][2] @group @subgroup
Code Owner entries
Each Code Owner entry includes a path followed by one or more owners.
README.md @username1
NOTE: If an entry is duplicated in a section, the last entry is used from each section.
Relative paths
If a path does not start with a /
, the path is treated as if it starts with
a globstar. README.md
is treated the same way as /**/README.md
:
# This will match /README.md, /internal/README.md, /app/lib/README.md
README.md @username
# This will match /internal/README.md, /docs/internal/README.md, /docs/api/internal/README.md
internal/README.md
Absolute paths
If a path starts with a /
it matches the root of the repository.
# Matches only the file named `README.md` in the root of the repository.
/README.md
# Matches only the file named `README.md` inside the `/docs` directory.
/docs/README.md
Directory paths
If a path ends with /
, the path matches any file in the directory.
# This is the same as `/docs/**/*`
/docs/
Wildcard paths
Wildcards can be used to match one of more characters of a path.
# Any markdown files in the docs directory
/docs/*.md @username
# /docs/index file of any filetype
# For example: /docs/index.md, /docs/index.html, /docs/index.xml
/docs/index.* @username
# Any file in the docs directory with 'spec' in the name.
# For example: /docs/qa_specs.rb, /docs/spec_helpers.rb, /docs/runtime.spec
/docs/*spec* @username
# README.md files one level deep within the docs directory
# For example: /docs/api/README.md
/docs/*/README.md @username
Globstar paths
Globstars (**
) can be used to match zero or more directories and subdirectories.
# This will match /docs/index.md, /docs/api/index.md, /docs/api/graphql/index.md
/docs/**/index.md
Entry owners
Entries must be followed by one or more owner. These can be groups, subgroups, and users. Order of owners is not important.
/path/to/entry.rb @group
/path/to/entry.rb @group/subgroup
/path/to/entry.rb @user
/path/to/entry.rb @group @group/subgroup @user
Groups as entry owners
Groups and subgroups can be owners of an entry. Each entry can be owned by one or more owners. For more details see the Add a group as a Code Owner.
/path/to/entry.rb @group
/path/to/entry.rb @group/subgroup
/path/to/entry.rb @group @group/subgroup
Users as entry owners
Users can be owners of an entry. Each entry can be owned by one or more owners.
/path/to/entry.rb @username1
/path/to/entry.rb @username1 @username2
Error handling in Code Owners
Error validation introduced in GitLab 16.3.
Entries with spaces
Paths containing whitespace must be escaped with backslashes: path\ with\ spaces/*.md
.
Without the backslashes, the path after the first whitespace is parsed as an owner.
GitLab the parses folder with spaces/*.md @group
into
path: "folder", owners: " with spaces/*.md @group"
.
Unparsable sections
If a section heading cannot be parsed, the section is:
- Parsed as an entry.
- Added to the previous section.
- If no previous section exists, the section is added to the default section.
For example, this file is missing a square closing bracket:
* @group
[Section name
docs/ @docs_group
GitLab recognizes the heading [Section name
as an entry. The default section includes 3 rules:
- Default section
-
*
owned by@group
-
[Section
owned byname
-
docs/
owned by@docs_group
-
This file contains an unescaped space between the words Section
and name
.
GitLab recognizes the intended heading as an entry:
[Docs]
docs/**/* @group
[Section name]{2} @group
docs/ @docs_group
The [Docs]
section then includes 3 rules:
-
docs/**/*
owned by@group
-
[Section
owned byname]{2} @group
-
docs/
owned by@docs_group
Malformed owners
Each entry must contain 1 or more owners to be valid, malformed owners are ignored.
For example /path/* @group user_without_at_symbol @user_with_at_symbol
is owned by @group
and @user_with_at_symbol
.
Inaccessible or incorrect owners
Inaccessible or incorrect owners are ignored. For example, if @group
, @username
,
and example@gitlab.com
are accessible on the project and we create an entry:
* @group @grou @username @i_left @i_dont_exist example@gitlab.com invalid@gitlab.com
GitLab ignores @grou
, @i_left
, @i_dont_exist
, and invalid@gitlab.com
.
For more information on who is accessible, see Add a group as a Code Owner.
Zero owners
If an entry includes no owners, or zero accessible owners exist, the entry is invalid. Because this rule can never be satisfied, GitLab auto-approves it in merge requests.
NOTE:
When a protected branch has Require code owner approval
enabled, rules with
zero owners are still honored.
Less than 1 required approval
When defining the number of approvals for a section,
the minimum number of approvals is 1
. Setting the number of approvals to
0
results in GitLab requiring one approval.
CODEOWNERS
file
Example # This is an example of a CODEOWNERS file.
# Lines that start with `#` are ignored.
# app/ @commented-rule
# Specify a default Code Owner by using a wildcard:
* @default-codeowner
# Specify multiple Code Owners by using a tab or space:
* @multiple @code @owners
# Rules defined later in the file take precedence over the rules
# defined before.
# For example, for all files with a filename ending in `.rb`:
*.rb @ruby-owner
# Files with a `#` can still be accessed by escaping the pound sign:
\#file_with_pound.rb @owner-file-with-pound
# Specify multiple Code Owners separated by spaces or tabs.
# In the following case the CODEOWNERS file from the root of the repo
# has 3 Code Owners (@multiple @code @owners):
CODEOWNERS @multiple @code @owners
# You can use both usernames or email addresses to match
# users. Everything else is ignored. For example, this code
# specifies the `@legal` and a user with email `janedoe@gitlab.com` as the
# owner for the LICENSE file:
LICENSE @legal this_does_not_match janedoe@gitlab.com
# Use group names to match groups, and nested groups to specify
# them as owners for a file:
README @group @group/with-nested/subgroup
# End a path in a `/` to specify the Code Owners for every file
# nested in that directory, on any level:
/docs/ @all-docs
# End a path in `/*` to specify Code Owners for every file in
# a directory, but not nested deeper. This code matches
# `docs/index.md` but not `docs/projects/index.md`:
/docs/* @root-docs
# Include `/**` to specify Code Owners for all subdirectories
# in a directory. This rule matches `docs/projects/index.md` or
# `docs/development/index.md`
/docs/**/*.md @root-docs
# This code makes matches a `lib` directory nested anywhere in the repository:
lib/ @lib-owner
# This code match only a `config` directory in the root of the repository:
/config/ @config-owner
# If the path contains spaces, escape them like this:
path\ with\ spaces/ @space-owner
# Code Owners section:
[Documentation]
ee/docs @docs
docs @docs
# Use of default owners for a section. In this case, all files (*) are owned by
the dev team except the README.md and data-models which are owned by other teams.
[Development] @dev-team
*
README.md @docs-team
data-models/ @data-science-team
# This section is combined with the previously defined [Documentation] section:
[DOCUMENTATION]
README.md @docs