PullApprove expressions allow you to write powerful, custom rules to design your workflow.

Expressions are evaluated in a Python environment, using the provided functions and variables. You can use human-readable operators like "in", "not in", "and", and, "or".

The same syntax powers the group conditions, pullapprove_conditions, and notification filters.

Single vs multi-line

Most basic expressions can be written in a single line. In YAML, you'll typically want to surround these with quotes since you'll have quotes inside.

version: 3
    - '"security" in labels'

When you're comparing lots of values, it may be easier to write the expression over multiple lines.

version: 3
- >
  contains_any_fnmatches(files, [


You can apply the same basic operations to most of the data we make available. Here are some common examples of how to use the available functions and variables.


Basic string comparisons will use fnmatch under the hood.

"*.js" in files or "frontend/*" in files

You can also use the contains_any_fnmatch function to check more paths at once without writing lots of "or" lines.

contains_any_fnmatches(files, [

Or globs!

contains_any_globs(files, [
glob("packages/**/*.js") in files

You can also chain the include and exclude methods to further filter down a list of files:



Use the base and head branch variables.

base.ref == "master"
base.ref != base.repo.default_branch
"feature" in head.ref


"bug" in labels
"sig-*" in labels
regex('.*/app') in labels

Other PullApprove groups

Use the state of preceding groups (above the current group in your config).

len(groups.approved) > 3
"admins" in groups.passing

Checks and statuses

The "checks" on a PR come in two forms: the Checks API,\ and the commit Statuses API.

The GitHub interface often combines these, making it hard to tell the difference. The best way to tell the difference is to look for the Checks tab on a PR page. For anything on that page (like GitHub Actions/Workflows), use the check_runs object. If you have a status that is not on that page, it will be found in statuses.

GitHub pull request "Checks" tab


"build" not in check_runs.failed


"*travis*" in statuses.succeeded

PR body or title

"WIP" in title
regex("WIP: .*") in title
"needs review" in body

PR size

changed_files > 30

PR author

author in ["internA", "internB"]
author_association == "FIRST_TIME_CONTRIBUTOR"


not mergeable

Git diff and files changed

contains_regex(files.patches, '(?m)^[\+\-].*dangerouslySetInnerHTML.*')


created_at < date('3 days ago')