Code Owners syntax and error handling

Tier: Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

The Code Owners configuration is stored in a CODEOWNERS file. This file determines who should review and approve changes.

This page describes the syntax and error handling used in CODEOWNERS files and provides examples of how to use them.

Example CODEOWNERS file

# This is an example of a CODEOWNERS file.
# Lines that start with `#` are ignored.

# 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 earlier rules.
# 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

# You can use both usernames or email addresses to match users:
LICENSE @legal janedoe@gitlab.com

# Use group names to match groups, and nested groups:
README @group @group/with-nested/subgroup

# Specify Code Owners for directories:
/docs/ @all-docs
/docs/* @root-docs
/docs/**/*.md @root-docs

# Match directories nested anywhere in the repository:
lib/ @lib-owner

# Match only a 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 sections:
[Documentation]
ee/docs    @docs
docs       @docs

[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

Code Owner file loading

The CODEOWNERS file is loaded from the target branch. GitLab checks these locations in your repository in this order:

  1. Root directory: ./CODEOWNERS
  2. Documentation directory: ./docs/CODEOWNERS
  3. .gitlab directory: ./.gitlab/CODEOWNERS

The first CODEOWNERS file found is used, and all others are ignored.

Pattern matching

GitLab uses File::fnmatch with the File::FNM_DOTMATCH and File::FNM_PATHNAME flags set for pattern matching:

  • The repository structure is treated like an isolated file system.
  • The patterns follow a subset of shell filename globbing rules, and are not regular expressions.
  • The File::FNM_DOTMATCH flag allows * to match dotfiles like .gitignore.
  • The File::FNM_PATHNAME flag prevents * from matching the / path separator.
  • ** matches directories recursively. For example, **/*.rb matches config/database.rb and app/controllers/users/stars_controller.rb.

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 [ ]:

[Section name]
/path/of/protected/file.rb @username
/path/of/protected/dir/ @group

Section headings

Section headings must have a name. For protected branches only, they can:

Examples:

# 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

Section names are case-insensitive and defined between square brackets. Sections with duplicate names are combined.

Code Owner entries

Each entry includes a path followed by one or more owners.

README.md @username1 @username2
note
If an entry is duplicated in a section, the last entry is used.

Path matching

Paths can be absolute, relative, wildcard, or globstar, and are matched against the repository root.

Relative paths

Paths without a leading / are treated as globstar paths:

# Matches /README.md, /internal/README.md, /app/lib/README.md
README.md @username

# Matches /internal/README.md, /docs/internal/README.md, /docs/api/internal/README.md
internal/README.md
note
When using globstar paths, be cautious of unintended matches. For example, README.md without a leading / matches any README.md file in any directory or subdirectory of the repository.

Absolute paths

Paths starting with / match from the repository root:

# # Matches only README.md in the root.
/README.md

# Matches only README.md inside the /docs directory.
/docs/README.md

Directory paths

Paths ending with / match any file in the directory:

# This is the same as `/docs/**/*`
/docs/

Wildcard paths

Use wildcards to match multiple characters:

# 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

Use ** to match zero or more directories recursively:

# Matches /docs/index.md, /docs/api/index.md, and /docs/api/graphql/index.md.
/docs/**/index.md

Entry owners

Entries must have one or more owners These can be groups, subgroups, and users.

/path/to/entry.rb @group
/path/to/entry.rb @group/subgroup
/path/to/entry.rb @user
/path/to/entry.rb @group @group/subgroup @user

For more information on adding groups as Code Owners, see Add a group as a Code Owner.

Error handling

History

Entries with spaces

Escape whitespace in paths with backslashes:

path\ with\ spaces/*.md @owner

Without escaping, GitLab parses folder with spaces/*.md @group as: path: "folder", owners: " with spaces/*.md @group".

Unparsable sections

If a section heading cannot be parsed, the section is:

  1. Parsed as an entry.
  2. Added to the previous section.
  3. If no previous section exists, the section is added to the default section.

After the default section

* @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 by name
    • docs/ owned by @docs_group

After a named section

[Docs]
docs/**/* @group

[Section name
docs/ @docs_group

GitLab recognizes the heading [Section name as an entry. The [Docs] section includes 3 rules:

  • docs/**/* owned by @group
  • [Section owned by name
  • docs/ owned by @docs_group

Malformed owners

Each entry must contain one or more owners. Malformed owners are invalid and ignored:

/path/* @group user_without_at_symbol @user_with_at_symbol

This entry is owned by @group and @user_with_at_symbol.

Inaccessible or incorrect owners

GitLab ignores inaccessible or incorrect owners. For example:

* @group @grou @username @i_left @i_dont_exist example@gitlab.com invalid@gitlab.com

If only @group, @username, and example@gitlab.com are accessible, GitLab ignores the others.

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.

Minimum approvals

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.