Mergeable¶
Mergeable helps automate your team’s GitHub workflow without a single line of code.
Usage¶
- Install the Mergeable GitHub App.
- Create your recipe(s) using Configuration or check out some ready to use examples at Recipes.
- Commit and push the recipes to your repository at
.github/mergeable.yml
- (Optional) You can also create a default configuration for an organisation by creating a repo called .github and adding your file there. See Organisation-wide defaults for details.
Note
You can also deploy to your own server. See Deploying
Configuration¶
Mergeable is highly configurable.
First, you’ll need to start by creating a .github/mergeable.yml
file in your repository.
Hint
Check out our Recipes page for examples and most commonly used settings
Next, we’ll go into how the configuration is structured.
Basics¶
Mergeable configuration consists of an array of independent recipes where each recipe needs to have the following properties:
- when:
- specify webhook event(s) in which to process the validation
- name:
- a friendly name which appears on the PR for the associated check
- filter:
- specify a series of optional filters to be checked and only runs validators if they are passing
- validate:
- specify a series of validator to be checked
- pass:
- specify a series of action to execute if the validation suite returned a pass
- fail:
- specify a series of action to execute if the validation suite returned a fail
- error:
- specify a series of action to execute if the validation suite returned a error
Each recipe appears as a separate check in the pull request.
Here is a full example of how a recipe looks -
version: 2
mergeable:
- when: {{event}}, {{event}} # can be one or more
name: check name A
filter:
# list of filters (optional). Specify one or more.
- do: {{filter}}
{{option}}: # name of an option supported by the validator.
{{sub-option}}: {{value}} # an option will have one or more sub-options.
validate:
# list of validators. Specify one or more.
- do: {{validator}}
{{option}}: # name of an option supported by the validator.
{{sub-option}}: {{value}} # an option will have one or more sub-options.
pass: # list of actions to be executed if all validation passes. Specify one or more. Omit this tag if no actions are needed.
- do: {{action}}
fail: # list of actions to be executed when at least one validation fails. Specify one or more. Omit this tag if no actions are needed.
- do: {{action}}
error: # list of actions to be executed when at least one validator throws an error. Specify one or more. Omit this tag if no actions are needed.
- do: {{action}}
- when: {{event}}, {{event}} # example for second recipe
name: check name B
filter:
# list of filters (optional). Specify one or more.
- do: {{filter}}
{{option}}: # name of an option supported by the validator.
{{sub-option}}: {{value}} # an option will have one or more sub-options.
validate:
# list of validators. Specify one or more.
- do: {{validator}}
{{option}}: # name of an option supported by the validator.
{{sub-option}}: {{value}} # an option will have one or more sub-options.
pass: # list of actions to be executed if all validation passes. Specify one or more. Omit this tag if no actions are needed.
- do: {{action}}
fail: # list of actions to be executed when at least one validation fails. Specify one or more. Omit this tag if no actions are needed.
- do: {{action}}
error: # list of actions to be executed when at least one validator throws an error. Specify one or more. Omit this tag if no actions are needed.
- do: {{action}}
Note
There are some default actions that’ll be automatically applied based on the events specified
Filters¶
Filters are checks that mergeable will process in order to determine if validator will be executed.
Note
Each filter have certain events that it can support, so keep an eye out for them.
Hint
Don’t see an filter that should be on here? Let us know by creating an issue on github
Filter List
Author¶
- do: author
must_include:
regex: 'user-1'
message: 'Custom include message...'
must_exclude:
regex: 'user-2'
message: 'Custom exclude message...'
team: 'org/team-slug' # verify that the author is in the team
# all of the message sub-option is optional
you can use and
and or
options to create more complex filters
- do: author
and:
- must_exclude:
regex: 'bot-user-1'
message: 'Custom message...'
or:
- must_include:
regex: 'user-1'
message: 'Custom message...'
- must_include:
regex: 'user-2'
message: 'Custom message...'
you can also nest and
and or
options
- do: author
and:
- or:
- must_include:
regex: 'user-1'
message: 'Custom message...'
- must_include:
regex: 'user-2'
message: 'Custom message...'
- must_exclude:
regex: 'bot-user-1'
message: 'Custom message...'
Supported Events:
'pull_request.*', 'pull_request_review.*'
Repository¶
- do: repository
visibility: 'public' # Can be public or private
name:
must_include:
regex: 'my-repo-name'
must_exclude:
regex: 'other-repo-name'
topics:
must_include:
regex: 'my-topic'
message: 'Custom message...'
must_exclude:
regex: 'other-topic'
message: 'Custom message...'
# all of the message sub-option is optional
you can use and
and or
options to create more complex filters
- do: repository
topics:
and:
- must_include:
regex: 'topic-1'
message: 'Custom message...'
- must_include:
regex: 'topic-2'
message: 'Custom message...'
or:
- must_include:
regex: 'topic-3'
message: 'Custom message...'
- must_include:
regex: 'topic-4'
message: 'Custom message...'
you can also nest and
and or
options
- do: repository
topics:
and:
- or:
- must_include:
regex: 'topic-1'
message: 'Custom message...'
- must_include:
regex: 'topic-2'
message: 'Custom message...'
- must_include:
regex: 'topic-3'
message: 'Custom message...'
Supported Events:
'pull_request.*', 'pull_request_review.*'
Payload¶
Check against any available fields within the payload, each event can have different field, please refer to `github API documentation<https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads>`_ for available fields.
An example to check if a pull_request_review event has state of changes_requested
- do: payload
review:
state:
must_include:
regex: 'changes_requested'
To check if a pull_request event is not a draft
- do: payload
pull_request:
draft:
boolean:
match: false
An example to check if a pull_request event has a label named foo
- do: payload
pull_request:
labels:
must_include:
regex: 'foo'
key: 'name'
Each field must be checked using one of the following options
boolean:
match: true/false
must_include:
regex: 'This text must be included'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
key: 'name' # Optional. If checking an array of objects, this specifies the key to check.
must_exclude:
regex: 'Text to exclude'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
key: 'name' # Optional. If checking an array of objects, this specifies the key to check.
Supported Events:
'pull_request.*', 'pull_request_review.*', issues.*'
Validators¶
Validators are checks that mergeable will process in order to determine whether an action should be executed.
Note
Each validator have certain events that it can support, so keep an eye out for them.
Hint
Don’t see an validator that should be on here? Let us know by creating an issue on github
Validator List
Age¶
- do: age // validate based on the age of PR
created_at:
days: 1
message: 'PR needs to at least 1 day old in order to merge' # optional, custom message to display if the validation fails
updated_at:
days: 1
message: 'PR needs to be update free for 1 day before merging' # optional, custom message to display if the validation fails
Supported Events:
'pull_request.*', 'pull_request_review.*',
Approvals¶
- do: approvals
min:
count: 2 # Number of minimum reviewers. In this case 2.
message: 'Custom message...'
required:
reviewers: [ user1, user2 ] # list of github usernames required to review
owners: true # Optional boolean. When true, the file .github/CODEOWNERS is read and owners made required reviewers
assignees: true # Optional boolean. When true, PR assignees are made required reviewers.
requested_reviewers: true # Optional boolean. When true, all the requested reviewer's approval is required
message: 'Custom message...'
block:
changes_requested: true # If true, block all approvals when one of the reviewers gave 'changes_requested' review
message: 'Custom message...'
limit:
teams: ['org/team_slug'] # when the option is present, only the approvals from the team members will count
users: ['user1', 'user2'] # when the option is present, approvals from users in this list will count
owners: true # Optional boolean. When true, the file .github/CODEOWNER is read and only owners approval will count
exclude:
users: ['bot1', 'bot2'] # when the option is present, approvals from users in this list will NOT count
Note
in limit
options, if more than one sub option is present, the union of the results will be used.
Note
If you receive an error for `Resource not accessible by integration’ for Owners file, it means you haven’t given mergeable read file permission
Note
owners
file now support teams as well, make sure to use @organization/team-slug format.
Supported Events:
'pull_request.*', 'pull_request_review.*'
Assignee¶
- do: assignee
max:
count: 2 # There should not be more than 2 assignees
message: 'test string' # this is optional
min:
count: 2 # min number of assignees
message: 'test string' # this is optional
Supported Events:
'pull_request.*', 'pull_request_review.*', 'issues.*'
Author¶
- do: author
must_include:
regex: 'user-1'
message: 'Custom include message...'
must_exclude:
regex: 'user-2'
message: 'Custom exclude message...'
team: 'org/team-slug' # verify that the author is in the team
# all of the message sub-option is optional
you can use and
and or
options to create more complex filters
- do: author
and:
- must_exclude:
regex: 'bot-user-1'
message: 'Custom message...'
or:
- must_include:
regex: 'user-1'
message: 'Custom message...'
- must_include:
regex: 'user-2'
message: 'Custom message...'
you can also nest and
and or
options
- do: author
and:
- or:
- must_include:
regex: 'user-1'
message: 'Custom message...'
- must_include:
regex: 'user-2'
message: 'Custom message...'
- must_exclude:
regex: 'bot-user-1'
message: 'Custom message...'
Supported Events:
'pull_request.*', 'pull_request_review.*'
BaseRef¶
- do: baseRef
must_include:
regex: 'master|feature-branch1'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
must_exclude:
regex: 'feature-branch2'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
mediaType: # Optional. Required by status.* events to enable the groot preview on some Github Enterprise servers
previews: 'array'
Simple example:
- do: baseRef
must_exclude:
regex: 'master'
message: 'Merging into repo:master is forbidden'
Example with groot preview enabled (for status.* events on some older Github Enterprise servers)
- do: baseRef
must_include:
regex: 'master|main'
message: 'Auto-merging is only enabled for default branch'
mediaType:
previews:
- groot
Supported Events:
'pull_request.*', 'pull_request_review.*', 'check_suite.*', status.*
Change set¶
- do: changeset # validate against the files in the PR
no_empty:
enabled: false # Cannot be empty when true.
message: 'Custom message...'
must_include:
regex: 'yarn.lock'
message: 'Custom message...'
must_exclude:
regex: 'package.json'
message: 'Custom message...'
begins_with:
match: 'A String' # or array of strings
message: 'Some message...'
ends_with:
match: 'A String' # or array of strings
message: 'Come message...'
min:
count: 2 # min number of files in a PR
message: 'Custom message...'
max:
count: 2 # max number of files in a PR
message: 'Custom message...'
files: # status of files to be included in changeset. If no 'files' option is provided, all files are included.
added: true # default: false. If true, added files are included.
modified: false # default: false. If true, modified files are included.
removed: true # default: false. If true, deleted files are included.
# note that setting file status sub-options (added, modified, removed) to false is optional.
# all of the message sub-option is optional
you can use and
and or
options to create more complex validations
- do: changeset # validate against the files in the PR
and:
- must_include:
regex: 'doc/.*'
message: 'Custom message...'
- must_include:
regex: 'changelog.md'
message: 'Custom message...'
or:
- must_include:
regex: 'package-lock.json'
message: 'Custom message...'
- must_include:
regex: 'yarn.lock'
message: 'Custom message...'
you can also nest and
and or
options
- do: changeset # validate against the files in the PR
and:
- or:
- must_include:
regex: 'package-lock.json'
message: 'Custom message...'
- must_include:
regex: 'package.json'
message: 'Custom message...'
- must_include:
regex: 'yarn.lock'
message: 'Custom message...'
Supported Events:
'pull_request.*', 'pull_request_review.*'
Commit¶
- do: commit
message:
regex: '^(feat|docs|chore|fix|refactor|test|style|perf)(\(\w+\))?:.+$'
message: 'Custom message' # Semantic release conventions must be followed
skip_merge: true # Optional, Default is true. Will skip commit with message that includes 'Merge'
oldest_only: false # Optional, Default is false. Only check the regex against the oldest commit
newest_only: false # Optional, Default is false. Only check the regex against the newest commit
single_commit_only: false # Optional, Default is false. only process this validator if there is one commit
message_type: '' # Optional, only check regex against the field specified. Default is '', which processes the 'message' field. Can also be set to 'author_email' or 'committer_email'
jira:
regex: '[A-Z][A-Z0-9]+-\d+'
regex_flag: none
message: 'The Jira ticket does not exist'
Supported Events:
'pull_request.*', 'pull_request_review.*'
Contents¶
- do: contents
files: # determine which files contents to validate
pr_diff: true # If true, validator will grab all the added and modified files in the head of the PR
ignore: ['.github/mergeable.yml'] # Optional, default ['.github/mergeable.yml'], pattern of files to ignore
must_include:
regex: 'yarn.lock'
message: 'Custom message...'
must_exclude:
regex: 'package.json'
message: 'Custom message...'
begins_with:
match: 'A String' # or array of strings
message: 'Some message...'
ends_with:
match: 'A String' # or array of strings
message: 'Come message...'
Supported Events:
'pull_request.*', 'pull_request_review.*'
Dependent¶
Validates that the files specified are all part of a pull request (added or modified).
- do: dependent
files: ['package.json', 'yarn.lock'] # list of files that are dependent on one another and must all be part of the changes in a PR.
message: 'Custom message...' # this is optional, a default message is used when not specified.
Alternatively, to validate dependent files only when a specific file is part of the pull request, use the changed option:
- do: dependent
changed:
file: package.json
files: ['package-lock.json', 'yarn.lock']
message: 'Custom message...' # this is optional, a default message is used when not specified.
The above will validate that both the files package-lock.json and yarn.lock is part of the modified or added files if and only if package.json is part of the PR.
Supported Events:
'pull_request.*', 'pull_request_review.*'
Description¶
- do: description
no_empty:
enabled: false # Cannot be empty when true.
message: 'Custom message...' # this is optional, a default message is used when not specified.
must_include:
regex: '### Goals|### Changes'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: >
Please describe the goals (why) and changes (what) of the PR.
# message is is optional, a default message is used when not specified.
must_exclude:
regex: 'DO NOT MERGE'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...' # optional
begins_with:
match: '### Goals' # or array of strings
message: 'Some message...' #optional
ends_with:
match: 'Any last sentence' # array of strings
message: 'Come message...' # optional
jira:
regex: '[A-Z][A-Z0-9]+-\d+'
regex_flag: none
message: 'The Jira ticket does not exist'
you can use and
and or
options to create more complex validations
- do: description
and:
- must_include:
regex: '### Goals'
message: 'Custom message...'
- must_include:
regex: '### Changes'
message: 'Custom message...'
or:
- must_include:
regex: '### Bug Description'
message: 'Custom message...'
- must_include:
regex: '### Feature Description'
message: 'Custom message...'
you can also nest and
and or
options
- do: description
and:
- or:
- must_include:
regex: '### Bug Description'
message: 'Custom message...'
- must_include:
regex: '### Feature Description'
message: 'Custom message...'
- must_include:
regex: '### Changes'
message: 'Custom message...'
Supported Events:
'pull_request.*', 'pull_request_review.*', 'issues.*'
HeadRef¶
- do: headRef
must_include:
regex: 'feature-branch1'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
must_exclude:
regex: 'feature-branch2'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
jira:
regex: '[A-Z][A-Z0-9]+-\d+'
regex_flag: none
message: 'The Jira ticket does not exist'
Simple example:
- do: headRef
must_include:
regex: '^(feature|hotfix)\/.+$'
message: |
Your pull request doesn't adhere to the branch naming convention described <a href="some link">there</a>!k
Supported Events:
'pull_request.*', 'pull_request_review.*'
Label¶
- do: label
no_empty:
enabled: false # Cannot be empty when true.
message: 'Custom message...'
must_include:
regex: 'type|chore|wont'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
must_exclude:
regex: 'DO NOT MERGE'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
begins_with:
match: 'A String' # or array of strings
message: 'Some message...'
ends_with:
match: 'A String' # or array of strings
message: 'Come message...'
jira:
regex: '[A-Z][A-Z0-9]+-\d+'
regex_flag: none
message: 'The Jira ticket does not exist'
# all of the message sub-option is optional
- do: label
and:
- must_include:
regex: 'big|medium|small'
message: 'Custom message...'
- must_include:
regex: 'type|chore|wont'
message: 'Custom message...'
or:
- must_include:
regex: 'Ready to merge'
message: 'Custom message...'
- must_include:
regex: 'DO NOT MERGE'
message: 'Custom message...'
you can also nest and
and or
options
- do: label
and:
- or:
- must_include:
regex: 'feat|fix|chore'
message: 'Custom message...'
- must_include:
regex: 'major|minor|patch'
message: 'Custom message...'
- must_include:
regex: 'Ready to merge'
message: 'Custom message...'
Supported Events:
'pull_request.*', 'pull_request_review.*', 'issues.*'
Milestone¶
- do: milestone
no_empty:
enabled: true # Cannot be empty when true.
message: 'Custom message...'
must_include:
regex: 'type|chore|wont'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
must_exclude:
regex: 'DO NOT MERGE'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
begins_with:
match: 'A String' # array of strings
message: 'Some message...'
ends_with:
match: 'A String' # array list of strings
message: 'Come message...'
jira:
regex: '[A-Z][A-Z0-9]+-\d+'
regex_flag: none
message: 'The Jira ticket does not exist'
# all of the message sub-option is optional
- do: milestone
and:
- must_include:
regex: 'V1'
message: 'Custom message...'
- must_include:
regex: 'October'
message: 'Custom message...'
or:
- must_include:
regex: 'V2'
message: 'Custom message...'
- must_include:
regex: 'Non breaking Changes'
message: 'Custom message...'
you can also nest and
and or
options
- do: milestone
and:
- or:
- must_include:
regex: 'V1'
message: 'Custom message...'
- must_include:
regex: 'September'
message: 'Custom message...'
- must_include:
regex: 'V2'
message: 'Custom message...'
Note
When a closing keyword is used in the description of a pull request. The annotated issue will be validated against the conditions as well.
Supported Events:
'pull_request.*', 'pull_request_review.*', 'issues.*'
Project¶
- do: project
must_include:
regex: 'type|chore|wont'
message: 'Custom message...'
Note
When a closing keyword is used in the description of a pull request. The annotated issue will be validated against the conditions as well.
Supported Events:
'pull_request.*', 'pull_request_review.*', 'issues.*'
Size¶
size
validates that the size of changes in the pull request conform to a specified limit. We can pass in three options: total
, additions
or deletions
. Each of this take in a count and message.
Validates that the files specified are all part of a pull request (added or modified).
- do: size
lines:
total:
count: 500
message: Change is very large. Should be under 500 lines of additions and deletions.
additions:
count: 250
message: Change is very large. Should be under 250 lines of additions
deletions:
count: 500
message: Change is very large. Should be under 250 lines of deletions.
ignore_comments: false #if true, comments will not be counted toward the lines count
max
is an alias for total, so the below configuration is still valid.
Warning
currently ignore_comments
feat only works with ‘.js’ and ‘.py’ extensions, if you have request for more file extensions please create a ticket here
- do: size
lines:
max:
count: 500
message: Change is very large. Should be under 500 lines of additions and deletions.
It also supports an ignore
or match
setting to allow excluding or including certain files from the total size (e.g. for ignoring automatically generated files that increase the size a lot).
This option supports glob patterns, so you can provide either the path to a specific file or ignore whole patterns:
- do: size
match: ['src']
ignore: ['package-lock.json', 'src/tests/__snapshots__/**', 'docs/*.md']
lines:
total:
count: 500
message: Change is very large. Should be under 500 lines of additions and deletions
In case both match
and ignore
are specified, the match
filters are applied first.
Note that the glob functionality is powered by the minimatch library. Please see their documentation for details on how glob patterns are handled and possible discrepancies with glob handling in other tools.
The size validator currently excludes from the size count any files that were completely deleted in the PR.
Supported Events:
'pull_request.*', 'pull_request_review.*'
Stale¶
- do: stale
days: 20 # number of days ago.
type: pull_request, issues # what items to search for.
ignore_drafts: true # if set to true, the stale check will ignore draft items
ignore_milestones: true # if set to true, the stale check will ignore items that have an associated milestone
ignore_projects: true # if set to true, the stale check will ignore items that have an associated project
label: # optional property to filter the items that are actioned upon
match: ['label1_to_match', 'label2_to_match'] # only items with matching labels will be actioned upon and marked as stale
ignore: ['label1_to_ignore', 'label2_to_ignore'] # items with these labels will be ignored and not marked as stale
time_constraint: # Optional, run the validator only if it in within the time constraint
time_zone: 'America/Los_Angeles' # Optional, UTC time by default, for valid timezones see `here <https://momentjs.com/timezone/>`_
hours_between: ['9', '17'] # Optional, 24 hours by default, run only if [0] >= Hour Now <= [1]
days_of_week: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'] # Optional, 7 days a week by default, specific the days of the week in which to run the validator
Note
This is a special use case. The schedule event runs on an interval. When used with stale, it will search for issues and/or pull request that are n days old. See a full example »
Supported Events:
'schedule.repository'
Title¶
- do: title
no_empty:
enabled: true # Cannot be empty when true. A bit redundant in this case since GitHub don't really allow it. :-)
message: 'Custom message...'
must_include:
regex: 'doc|feat|fix|chore'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
must_exclude:
regex: 'DO NOT MERGE|WIP'
regex_flag: 'none' # Optional. Specify the flag for Regex. default is 'i', to disable default use 'none'
message: 'Custom message...'
begins_with:
match: ['doc','feat','fix','chore']
message: 'Some message...'
ends_with:
match: 'A String' # or array of strings
message: 'Come message...'
# all of the message sub-option is optional
jira:
regex: '[A-Z][A-Z0-9]+-\d+'
regex_flag: none
message: 'The Jira ticket does not exist'
- do: title
and:
- must_include:
regex: 'ISSUE-\d+'
message: 'Custom message...'
- must_include:
regex: 'type:.+'
message: 'Custom message...'
or:
- must_include:
regex: 'feat|chore|fix'
message: 'Custom message...'
- must_include:
regex: 'major|minor|patch'
message: 'Custom message...'
you can also nest and
and or
options
- do: title
and:
- or:
- must_include:
regex: 'feat|fix|chore'
message: 'Custom message...'
- must_include:
regex: 'major|minor|patch'
message: 'Custom message...'
- must_include:
regex: 'ISSUE-\d+'
message: 'Custom message...'
Supported Events:
'pull_request.*', 'pull_request_review.*', 'issues.*'
Options¶
These Options are used with validators.
Options List
Boolean¶
boolean
can be used to validate if the input is exactly true or false. This does not pass truthy values.
For example, if the pull_request.draft value is false:
{
"action": "opened",
"number": 2,
"pull_request": {
"draft": false
This will pass validation, because the value of draft exactly matches false:
- do: payload
pull_request:
draft:
boolean:
match: false
message: 'Custom message...' # this is optional, a default message is used when not specified.
Param | Description | Required | Default Message |
---|---|---|---|
match | Bool value to check for | Yes | |
message | Message to show if the validation fails | No | The [INPUT NAME] must be [match] |
Supported Filters:
'payload'
BeginsWith¶
begins_with
can be used to validate inputs if begin with given string
- do: milestone
begins_with:
match: 'A String' # array of strings
message: 'Some message...'
Param | Description | Required | Default Message |
---|---|---|---|
match | message to validate input with | Yes | |
message | Message to show if the validation fails | No | [INPUT NAME] must begins with [MATCH] |
Supported Validators:
'changeset', 'content', 'description', 'label', 'milestone', 'title'
EndsWith¶
ends_with
can be used to validate inputs if end with given string
- do: milestone
ends_with:
match: 'A String' # array of strings
message: 'Some message...'
Param | Description | Required | Default Message |
---|---|---|---|
match | message to validate input with | Yes | |
message | Message to show if the validation fails | No | [INPUT NAME] must ends with [MATCH] |
Supported Validators:
'changeset', 'content', 'description', 'label', 'milestone', 'title'
Jira¶
jira
can be used to validate inputs string in Atlassian Jira with APIs
Note
This option work in self-hosted version only.
Env | Description | Required | Default |
---|---|---|---|
JIRA_PASSWORD | Password to authenticate with JIRA | Yes | |
JIRA_USERNAME | Username to authenticate with JIRA | Yes | |
JIRA_HOST | Host to authenticate with JIRA | yes | |
JIRA_PROTOCOL | Protocol to establish connection with JIRA | no | https |
JIRA_VERSION | JIRA API Version to use | no | 2 |
JIRA_STRICT_SSL | Force SSL while establishing connection | no | yes |
- do: headRef
jira:
regex: '[A-Z][A-Z0-9]+-\d+'
regex_flag: none
message: 'The Jira ticket does not valid'
Param | Description | Required | Default Message |
---|---|---|---|
regex | Regex enabled message to validate input with | Yes | |
message | Message to show if the validation fails | No | [INPUT NAME] does not include [REGEX] |
regex_flag | Regex flag to be used with regex param to validate inputs | No | i |
Supported Validators:
'commit', 'description', 'headRef', 'label', 'milestone', 'title'
Max¶
max
can be used to validate inputs length that is no more than given integer.
- do: assignee
max:
count: 2 # There should not be more than 2 assignees
message: 'test string' # this is optional
Param | Description | Required | Default Message |
---|---|---|---|
count | number to validate input’s length | Yes | |
message | Message to show if the validation fails | No | [INPUT NAME] count is more than [COUNT] |
Supported Validators:
'approvals', 'assignee', 'changeset', 'label'
Min¶
min
can be used to validate inputs length that isn’t less than given integer.
- do: assignee
min:
count: 2 # There should be more than 2 assignees
message: 'test string' # this is optional
Param | Description | Required | Default Message |
---|---|---|---|
count | number to validate input’s length | Yes | |
message | Message to show if the validation fails | No | [INPUT NAME] count is less than [COUNT] |
Supported Validators:
'approvals', 'assignee', 'changeset', 'label', 'size'
MustInclude¶
must_include
can be used to validate input that includes the given regex supported message.
- do: headRef
must_include:
regex: '^(feature|hotfix)\/.+$'
message: |
Your pull request doesn't adhere to the branch naming convention described <a href="some link">there</a>!k
You can also use an array of regex matchers. If any of them match, the validation will pass.
- do: headRef
must_include:
regex:
- "^feature"
- "^hotfix"
- "^fix"
message: |
Your pull request doesn't adhere to the branch naming convention described <a href="some link">there</a>!k
Param | Description | Required | Default Message |
---|---|---|---|
regex | Regex or array enabled message to validate input with | Yes | |
message | Message to show if the validation fails | No | [INPUT NAME] does not include [REGEX] |
regex_flag | Regex flag to be used with regex param to validate inputs | No | i |
Supported Validators:-
'baseRef', 'headRef', 'changeset', 'commit', 'content', 'description', 'label', 'milestone', 'project', 'title'
MustExclude¶
must_exclude
can be used to validate input that excludes the given regex supported message.
- do: headRef
must_exclude:
regex: '^(feature|hotfix)\/.+$'
message: |
Your pull request doesn't adhere to the branch naming convention described <a href="some link">there</a>!k
You can also use an array of regex matchers. If any of them match, the validation will fail.
- do: headRef
must_exclude:
regex:
- "^bug"
- "^breaking"
- "^test"
message: |
Your pull request doesn't adhere to the branch naming convention described <a href="some link">there</a>!k
Param | Description | Required | Default Message |
---|---|---|---|
regex | Regex or array enabled message to validate input with | Yes | |
message | Message to show if the validation fails | No | [INPUT NAME] does not include [REGEX] |
regex_flag | Regex flag to be used with regex param to validate inputs | No | i |
Supported Validators:-
'baseRef', 'headRef', 'changeset', 'content', 'description', 'label', 'milestone', 'title'
NoEmpty¶
no_empty
can be used to validate if input is not empty
- do: description
no_empty:
enabled: false # Cannot be empty when true.
message: 'Custom message...' # this is optional, a default message is used when not specified.
Param | Description | Required | Default Message |
---|---|---|---|
enabled | Bool value to enable/disable the option | Yes | |
message | Message to show if the validation fails | No | The [INPUT NAME] can’t be empty |
Supported Validators:
'changeset', 'description', 'label', 'milestone', 'title'
Required¶
required
can be used to validate if input meets the conditions given with params
- do: approvals
required:
reviewers: [ user1, user2 ] # list of github usernames required to review
owners: true # Optional boolean. When true, the file .github/CODEOWNERS is read and owners made required reviewers
assignees: true # Optional boolean. When true, PR assignees are made required reviewers.
requested_reviewers: true # Optional boolean. When true, all the requested reviewer's approval is required
message: 'Custom message...'
Param | Description | Required | Default Message |
---|---|---|---|
reviewers | An array value for github users/teams to be required to do the validation | No | [] |
owners | The file .github/CODEOWNERS is read and owners made required reviewers | No | [] |
assignees | PR assignees are made required reviewers. | No | [] |
requested_reviewers | All the requested reviewer’s approval is required | No | [] |
message | Message to show if the validation fails | No | [INPUT NAME] does not include [REGEX] |
Supported Validators:
'approvals'
Operators¶
These operators can help create more complex logic of validations
Operator List
And¶
And
, Or
, and Not
can be used to create more complex validation/filter check
filter:
- do: and
filter:
- do: author
must_include: 'user-1'
- do: repository
visibility: public
validate:
- do: and
validate:
- do: title
begins_with: '[WIP]'
- do: label
must_include: 'Ready to Merge'
you can also create nested And
, Or
, and Not
filter:
- do: and
filter:
- do: or
filter:
- do: author
must_include: 'user-1'
- do: author
must_include: 'user-2'
- do: repository
visibility: public
validate:
- do: and
validate:
- do: or
validate:
- do: title
begins_with: 'feat:'
- do: label
must_include: 'feature'
- do: label
must_include: 'Ready to Merge'
Or¶
And
, Or
, and Not
can be used to create more complex validation/filter check
filter:
- do: or
filter:
- do: author
must_include: 'user-1'
- do: repository
visibility: public
validate:
- do: or
validate:
- do: title
begins_with: '[WIP]'
- do: label
must_include: 'Ready to Merge'
you can also create nested And
, Or
, and Not
filter:
- do: and
filter:
- do: or
filter:
- do: author
must_include: 'user-1'
- do: author
must_include: 'user-2'
- do: repository
visibility: public
validate:
- do: and
validate:
- do: or
validate:
- do: title
begins_with: '[WIP]'
- do: label
must_include: '[WIP]'
- do: label
must_include: 'DO NOT MERGE'
Not¶
And
, Or
, and Not
can be used to create more complex validation/filter check
filter:
- do: not
filter:
- do: author
must_include: 'user-1'
- do: repository
visibility: public
validate:
- do: not
validate:
- do: title
begins_with: '[WIP]'
- do: label
must_include: 'Ready to Merge'
you can also create nested And
, Or
, and Not
filter:
- do: not
filter:
- do: or
filter:
- do: author
must_include: 'user-1'
- do: author
must_include: 'user-2'
validate:
- do: and
validate:
- do: not
validate:
- do: title
begins_with: 'feat:'
- do: label
must_include: 'feature'
- do: label
must_include: 'Ready to Merge'
Actions¶
Actions that mergeable is currently able to perform.
Hint
Don’t see an action that should be on here? Let us know by creating an issue on github
Assign¶
- do: assign
assignees: [ 'shine2lay', 'jusx', '@author' ] # only array accepted, use @author for PR/Issue author
Supported Events:
'pull_request.*', 'issues.*'
Check¶
Note
The logic for whether checks will be added by default is as follows: 1. If no action is provided in either pass, fail or error , add checks as default (to be backward compatible) 2. If only actions other than checks is provided, don’t add check as default (to support cases where checks are not wanted) 3. If checks is a part of the actions provided, all pass , fail and error cases will have checks (to prevent case where a check is hanged and never finish processing)
- do: checks # default pass case
status: 'success' # Can be: success, failure, neutral, cancelled, timed_out, or action_required
payload:
title: 'Mergeable Run have been Completed!'
summary: "All the validators have returned 'pass'! \n Here are some stats of the run: \n {{validationCount}} validations were ran"
You can pass in Handlebars template to show the details result of the run.
- do: checks # default fail case
status: 'failure' # Can be: success, failure, neutral, cancelled, timed_out, or action_required
payload:
title: 'Mergeable Run have been Completed!'
summary: |
### Status: {{toUpperCase validationStatus}}
Here are some stats of the run:
{{validationCount}} validations were ran.
{{passCount}} PASSED
{{failCount}} FAILED
text: "{{#each validationSuites}}\n
#### {{{statusIcon status}}} Validator: {{toUpperCase name}}\n
{{#each validations }} * {{{statusIcon status}}} ***{{{ description }}}***\n
Input : {{{details.input}}}\n
Settings : {{{displaySettings details.settings}}}\n
{{/each}}\n
{{/each}}"
- do: checks # default error case
status: 'action_required' # Can be: success, failure, neutral, cancelled, timed_out, or action_required
payload:
title: 'Mergeable found some errors!'
summary: |
### Status: {{toUpperCase validationStatus}}
Some or All of the validators have returned 'error' status, please check below for details
Here are some stats of the run: \n {{validationCount}} validations were ran.
{{passCount}} ***PASSED***
{{failCount}} ***FAILED***
{{errorCount}} ***ERRORED***
text: "{{#each validationSuites}}
#### {{{statusIcon status}}} Validator: {{toUpperCase name}}
Status {{toUpperCase status}}
{{#each validations }} * {{{statusIcon status}}} ***{{{ description }}}***
Input : {{{details.input}}}
Settings : {{{displaySettings details.settings}}}
{{#if details.error}}
Error : {{{details.error}}}
{{/if}}
{{/each}}
{{/each}}"
Note
if any of required fields title
, summary
or status
is missing, default values will be used
Note
checks will automatically re-run if the base branch has a modified config file
Supported Events:
The pull_request.closed event is not supported since it does not have meaningful use in the context of GitHub check API.
'pull_request.assigned', 'pull_request.auto_merge_disabled', 'pull_request.auto_merge_enabled', 'pull_request.converted_to_draft', 'pull_request.demilestoned', 'pull_request.dequeued', 'pull_request.edited', 'pull_request.enqueued', 'pull_request.labeled', 'pull_request.locked', 'pull_request.milestoned', 'pull_request.opened', 'pull_request.push_synchronize', 'pull_request.ready_for_review', 'pull_request.reopened', 'pull_request.review_request_removed', 'pull_request.review_requested', 'pull_request.synchronize', 'pull_request.unassigned', 'pull_request.unlabeled', 'pull_request.unlocked', 'pull_request_review.dismissed', 'pull_request_review.edited', 'pull_request_review.submitted'
Comment¶
- do: comment
payload:
body: >
Your very long comment can go here.
leave_old_comment: true # Optional, by default old comments are deleted, if true, old comments will be left alone
Supported Events:
'schedule.repository', 'pull_request.*', 'issues.*'
Merge¶
- do: merge
merge_method: 'merge' # Optional, default is 'merge'. Other options : 'rebase', 'squash'
# template variables for next two items come from result of https://docs.github.com/en/rest/reference/pulls#get-a-pull-request
# use triple curly braces to avoid html escaping
commit_title: '{{{ title }}} (#{{{ number }}})' # Optional, override commit title
commit_message: '{{{ body }}}' # Optional, override commit message
Supported Events:
'pull_request.*', 'pull_request_review.*', 'status.*', 'check_suite.*'
Labels¶
You can add, remove, and delete labels with one action.
Warning
Using multiple do: labels
in the same operation is not idempotent.
Use only one labels
action per block for accurate labeling.
You can add new labels, preserving existing ones
- do: labels
add: 'Ready for Review'
You can delete existing labels
- do: labels
delete: [ 'Ready for Review', 'Triage' ]
You can replace all current labels with new ones
- do: labels
replace: [ 'Triage', 'Needs Deploy' ]
You can also use any combination of these options. They can be listed in any
order, but the action is always evaluated in the order of replace
→ add
→ delete
.
- do: labels
replace: [ 'New Task', 'Not Useful' ]
add: [ 'Work in Progress', 'Needs Deploy' ]
delete: 'Not Useful'
# result: [ 'New Task', 'Work in Progress', 'Needs Deploy' ]
labels
and mode
¶
Warning
Using labels
with mode
is deprecated and will be removed in v3.
Use the add
, replace
, and delete
options above for labeling.
You can also add a set of the labels on an item
- do: labels
# if label doesn't exist, it'll be created
labels: [ 'Triage' ] # Only arrays are accepted
mode: 'add' # Optional , default is 'add'. Other options : 'replace', 'delete'
You can also replace all of the labels on an item with a given set of labels
- do: labels
# if label doesn't exist, it'll be created
labels: [ 'Triage' ] # Only arrays are accepted
mode: 'replace' # Replaces all of the labels with the above array of labels
You can also delete existing labels on an item and specify glob patterns when the mode is delete.
- do: labels
# if label doesn't exist, it'll be created
labels: [ 'feature-*' ] # All labels beginning with 'feature-' will be removed
mode: 'delete'
Note that the glob functionality is powered by the minimatch library. Please see their documentation for details on how glob patterns are handled and possible discrepancies with glob handling in other tools.
Supported Events:
'schedule.repository', 'pull_request.*', 'issues.*'
Request Review¶
You can request specific reviews from specific reviewers, teams, or both
- do: request_review
reviewers: ['name1', 'name2']
teams: ['developers'] # team names without organization
Supported Events:
'pull_request.*'
Reusable Configuration¶
YML has a feature called `Anchor<https://blog.daemonl.com/2016/02/yaml.html>`_ that allows you to create reusable parts in the config
Organisation-wide defaults¶
You can specify a default configuration to be applied across your GitHub organisation. This can help reduce how many configuration files you need to maintain and make it easier to get started with Mergeable.
To add a default configuration:
- Create a repository called
.github
in your organisation. - Create a file with the path
.github/mergeable.yml
in this repository.
The final path of the file (including the repo name) should be <YOUR_ORG>/.github/.github/mergeable.yml
Mergeable will now use this file as the default when it cannot find one in a given repository or PR. It determines the file to use in the following order:
- A
mergeable.yml
inside the PR if the PR originates from the same repository. If it originates from a fork it is not loaded for security reasons. See #406 for more details. - A
mergeable.yml
inside the repository the PR is for. - A
mergeable.yml
at<YOUR_ORG>/.github/.github/mergeable.yml
.
Note
If config file is changed in base branch, all the PR against the base branch will be re ran using the changed config file
Warning! this feature require mergeable have read
access to contents and needs to be listening for push
event
Why the weird default file path?¶
The Probots library that Mergeable uses automatically searches for config files
in a repo named .github
within the organisation.
The double nesting of the <YOUR_ORG>/.github/.github/mergeable.yml
default
file is unfortunately necessary. The GitHub app permissions model only lets you
specify a single path for your probot to access, so it must be the same as in
regular repositories.
Recipes¶
Work In Progress¶
Prevent accidental merging of Pull Requests that are work in progress by labeling it WIP or prefixing the title with the abbreviation.
version: 2
mergeable:
- when: pull_request.*
validate:
- do: title
must_exclude:
regex: ^\[WIP\]
- do: label
must_exclude:
regex: 'wip'
No Empty Description¶
Ensure all Pull Requests have a description so that reviewers have context.
version: 2
mergeable:
- when: pull_request.*
validate:
- do: description
no_empty:
enabled: true
message: Description matter and should not be empty. Provide detail with **what** was changed, **why** it was changed, and **how** it was changed.
Dependent Files¶
Certain files are related and you want to ensure that they are updated as part of the PR (i.e. if package.json is updated, so should yarn.lock and package-lock.json)
version: 2
mergeable:
- when: pull_request.*
validate:
- do: dependent
changed:
file: 'package.json' # also supports globs expressions
required: ['package-lock.json', 'yarn.lock'] # alias: `files` for backward compatibility
Must Include Milestone¶
Ensure that all Pull Requests have a milestone associated. Mergeable will also detect when you are `closing an issue<https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue>`_ that is associated with the specified milestone.
version: 2
mergeable:
- when: pull_request.*
validate:
- do: milestone
must_include:
regex: Release 1
Small PR Size¶
Size: Ensure that PRs don’t exceed a certain size in terms of lines changed (excluding file patterns specified with ignore
).
version: 2
mergeable:
- when: pull_request.*
validate:
- do: size
ignore: ['ignore_me.js', 'ignore_this_directory/*', '**/ignore_this_prefix*.js']
lines:
max:
count: 500
message: Change is very large. Should be under 500 lines of addtions and deletions.
Comment If Guidelines not met¶
Automatically create a comment when a new issue is openened to remind the author when the title does not follow conventions or is missing a label.
version: 2
mergeable:
- when: issues.opened
validate:
- do: title
begins_with:
match: ['AUTH', 'SOCIAL', 'CORE']
- do: label
must_include:
regex: bug|enhancement
fail:
- do: comment
payload:
body: >
The following problems were found with this issue:
- Title must begin with `AUTH`, `SOCIAL` or `CORE`
- The issue should either be labeled `bug` or `enhancement`
Check Stale PR and Issues¶
Detect issues and pull requests that are n days old (stale) and notify authors and collaborators by creating a comment.
version: 2
mergeable:
- when: schedule.repository
validate:
- do: stale
days: 20
type: pull_request, issues
pass:
- do: comment
payload:
body: This is old. Is it still relevant?
Greet a new contributor¶
Add a comment on a pull request when it is created
version: 2
mergeable:
- when: pull_request.opened
name: "Greet a contributor"
validate: []
pass:
- do: comment
payload:
body: >
Thanks for creating a pull request! A maintainer will review your changes shortly. Please don't be discouraged if it takes a while.
Auto-merge pull requests once all checks pass¶
This recipe relies on the fact that the main branch has been protected and only allows merges
when the required checks have passed or the required number of reviews/other conditions are met.
This basically means that mergeable
will merge the pull request as soon as it shows a green merged button
on Github.
Notice the blank validator which ensures that the merge event happens as soon as Github allows mergeable
to merge the pull request.
version: 2
mergeable:
- when: pull_request.*, pull_request_review.*, status.*, check_suite.*
name: "Automatically merge pull requests once it passes all checks"
validate: []
pass:
- do: merge
merge_method: "squash"
Approval check + title check if certain files are changed¶
Add 2 checks to the PR 1. Approval check - Checks whether the PR has been approved by certain people 2. Title should match a regex if certain files are changed. If no changes are made in those files, check should pass
version: 2
mergeable:
- when: pull_request.*, pull_request_review.*
name: 'Approval check'
validate:
- do: approvals
min:
count: 1
limit:
users: [ 'approverA', 'approverB' ]
- when: pull_request.*, pull_request_review.*
name: 'PR title check'
validate:
- do: or
validate:
- do: changeset
must_exclude:
regex: 'some/regex/for/those/certain/files/*'
- do: and
validate:
- do: changeset
must_include:
regex: 'some/regex/for/those/certain/files/*'
- do: title
begins_with:
match: [ 'some prefix' ]
Only run rules if PR is not a draft¶
Checks that the PR’s draft state is false before running actions.
version: 2
mergeable:
- when: pull_request.*, pull_request_review.*
name: 'Draft check'
validate:
- do: payload
pull_request:
draft:
boolean:
match: false
pass:
- do: comment
payload:
body: This PR is NOT a draft!
fail:
- do: comment
payload:
body: This PR is STILL a draft!
Allow commits only if they contain a Issue ID (like an Azure DevOps Work Item)¶
Checks that the PR’s draft state is false before running actions.
version: 2
mergeable:
- when: pull_request.*
validate:
- do: commit
message:
regex: '^(AB#[0-9]{1,})' #check if all commit messages begin with an AzDO Work Item
pass:
- do: comment
payload:
body: >
<h2>Successfully checked for Azure Work Item IDs in commits</h2>
<h3>All commits in your PR have Azure Board Work Item IDs. Ready for Review!</h3>
:+1:
- do: labels
add: 'Ready for Review'
fail:
- do: comment
payload:
body: >
:warning:
<h2>Azure Boards Work Item IDs missing in commits</h2>
<h3>Some commits messages were found not having the Azure Boards Work Item ID (AB#1234).</h3>
<h3>We will close this PR for now.</h3>
<h3>To resolve, please do one of the following</h3>
<ul>
<li>Identify your Azure Boards Work Item ID and <a href="https://gist.github.com/nepsilon/156387acf9e1e72d48fa35c4fabef0b4">amend your commits</a>. Then re-open the PR</li>
<li>In case you do not have a Work Item ID to reference, please discuss with your reviewer(s) for alternate options</li>
</ul>
- do: labels
add: 'Non-Compliant'
- do: close
Contributing¶
We need your help:
- Have an idea for a new feature? Please create a new issue and tell us!
- Fix a bug, implement a new validator or action and open a pull request!
Note
For development and testing, you’ll want to read Deploying.
CHANGELOG¶
named
recipe, #225CONFIG_PATH
env variable, #223