🕷️ Crawler Inspector

URL Lookup

Direct Parameter Lookup

Raw Queries and Responses

1. Shard Calculation

Query:
Response:
Calculated Shard: 184 (from laksa034)

2. Crawled Status Check

Query:
Response:

3. Robots.txt Check

Query:
Response:

4. Spam/Ban Check

Query:
Response:

5. Seen Status Check

ℹ️ Skipped - page is already crawled

đź“„
INDEXABLE
âś…
CRAWLED
20 days ago
🤖
ROBOTS SERVER UNREACHABLE
Failed to connect to robots server: Operation timed out after 2002 milliseconds with 0 bytes received

Page Info Filters

FilterStatusConditionDetails
HTTP statusPASSdownload_http_code = 200HTTP 200
Age cutoffPASSdownload_stamp > now() - 6 MONTH0.7 months ago
History dropPASSisNull(history_drop_reason)No drop reason
Spam/banPASSfh_dont_index != 1 AND ml_spam_score = 0ml_spam_score=0
CanonicalPASSmeta_canonical IS NULL OR = '' OR = src_unparsedNot set

Page Details

PropertyValue
URLhttps://docs.gitlab.com/ci/jobs/job_rules/
Last Crawled2026-03-17 19:17:14 (20 days ago)
First Indexed2025-02-21 02:20:29 (1 year ago)
HTTP Status Code200
Meta TitleSpecify when jobs run with rules | GitLab Docs
Meta DescriptionGitLab product documentation.
Meta Canonicalnull
Boilerpipe Text
Tier : Free, Premium, Ultimate Offering : GitLab.com, GitLab Self-Managed, GitLab Dedicated Use the rules keyword to include or exclude jobs in pipelines. Rules are evaluated in order until the first match. When a match is found, the job is either included or excluded from the pipeline, depending on the configuration. You cannot use dotenv variables created in job scripts in rules, because rules are evaluated before any jobs run. rules examples The following example uses if to define that the job runs in only two specific cases: yaml job : script : echo "Hello, Rules!" rules : - if : $CI_PIPELINE_SOURCE == "merge_request_event" when : manual allow_failure : true - if : $CI_PIPELINE_SOURCE == "schedule" If the pipeline is for a merge request, the first rule matches, and the job is added to the merge request pipeline with attributes of: when: manual (manual job) allow_failure: true (the pipeline continues running even if the manual job is not run) If the pipeline is not for a merge request, the first rule doesn’t match, and the second rule is evaluated. If the pipeline is a scheduled pipeline, the second rule matches, and the job is added to the scheduled pipeline. No attributes were defined, so it is added with: when: on_success (default) allow_failure: false (default) In all other cases, no rules match, so the job is not added to any other pipeline. Alternatively, you can define a set of rules to exclude jobs in a few cases, but run them in all other cases: yaml job : script : echo "Hello, Rules!" rules : - if : $CI_PIPELINE_SOURCE == "merge_request_event" when : never - if : $CI_PIPELINE_SOURCE == "schedule" when : never - when : on_success If the pipeline is for a merge request, the job is not added to the pipeline. If the pipeline is a scheduled pipeline, the job is not added to the pipeline. In all other cases, the job is added to the pipeline, with when: on_success . If you use a when clause as the final rule (not including when: never ), two simultaneous pipelines may start. Both push pipelines and merge request pipelines can be triggered by the same event (a push to the source branch for an open merge request). See how to avoid duplicate pipelines for more details. Run jobs for scheduled pipelines You can configure a job to be executed only when the pipeline has been scheduled. For example: yaml job:on-schedule : rules : - if : $CI_PIPELINE_SOURCE == "schedule" script : - make world job : rules : - if : $CI_PIPELINE_SOURCE == "push" script : - make build In this example, make world runs in scheduled pipelines, and make build runs in branch and tag pipelines. Skip jobs if the branch is empty Use rules:changes:compare_to to skip a job when the branch is empty, which saves CI/CD resources. The configuration compares the branch to the default branch, and if the branch: Doesn’t have changed files, the job doesn’t run. Has changed files, the job runs. For example, in a project with main as the default branch: yaml job : script : - echo "This job only runs for branches that are not empty" rules : - if : $CI_COMMIT_BRANCH changes : compare_to : 'refs/heads/main' paths : - '**/*' The rule for this job compares all files and paths in the current branch recursively ( **/* ) against the main branch. The rule matches and the job runs only when there are changes to the files in the branch. Run a job when a file is not present You can use rules: exists to configure a job to run only when a specific file does not exist. For example, to run a job in a merge request pipeline when the example.yml file does not exist: yaml job : script : echo "Hello, Rules!" rules : - exists : - "example_dir/example.yml" when : never - if : $CI_PIPELINE_SOURCE == "merge_request_event" In this example, if the example_dir/example.yml file exists in the branch, the job does not run. If the file does not exist, the job can run in merge request pipelines. Common if clauses with predefined variables rules:if clauses are commonly used with predefined CI/CD variables , especially CI_PIPELINE_SOURCE . The following example runs the job as a manual job in scheduled pipelines or in push pipelines (to branches or tags), with when: on_success (default). It does not add the job to any other pipeline type. yaml job : script : echo "Hello, Rules!" rules : - if : $CI_PIPELINE_SOURCE == "schedule" when : manual allow_failure : true - if : $CI_PIPELINE_SOURCE == "push" The following example runs the job as a when: on_success job in merge request pipelines and scheduled pipelines. It does not run in any other pipeline type. yaml job : script : echo "Hello, Rules!" rules : - if : $CI_PIPELINE_SOURCE == "merge_request_event" - if : $CI_PIPELINE_SOURCE == "schedule" Other commonly used if clauses: if: $CI_COMMIT_TAG : If changes are pushed for a tag. if: $CI_COMMIT_BRANCH : If changes are pushed to any branch. if: $CI_COMMIT_BRANCH == "main" : If changes are pushed to main . if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH : If changes are pushed to the default branch. if: $CI_COMMIT_BRANCH =~ /regex-expression/ : If the commit branch matches a regular expression. if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_TITLE =~ /Merge branch.*/ : If the commit branch is the default branch and the commit message title matches a regular expression. if: $CUSTOM_VARIABLE == "value1" : If the custom variable CUSTOM_VARIABLE is exactly value1 . Run jobs only in specific pipeline types You can use predefined CI/CD variables with rules to choose which pipeline types jobs should run for. The following table lists some of the variables that you can use, and the pipeline types the variables can control for: Branch pipelines that run for Git push events to a branch, like new commits or tags. Tag pipelines that run only when a new Git tag is pushed to a branch. Merge request pipelines that run for changes to a merge request, like new commits or selecting Run pipeline in a merge request’s pipelines tab. Scheduled pipelines. Variables Branch Tag Merge request Scheduled CI_COMMIT_BRANCH Yes Yes CI_COMMIT_TAG Yes Yes, if the scheduled pipeline is configured to run on a tag. CI_PIPELINE_SOURCE = push Yes Yes CI_PIPELINE_SOURCE = schedule Yes CI_PIPELINE_SOURCE = merge_request_event Yes CI_MERGE_REQUEST_IID Yes For example, to configure a job to run for merge request pipelines and scheduled pipelines, but not branch or tag pipelines: yaml job1 : script : - echo rules : - if : $CI_PIPELINE_SOURCE == "merge_request_event" - if : $CI_PIPELINE_SOURCE == "schedule" - if : $CI_PIPELINE_SOURCE == "push" when : never CI_PIPELINE_SOURCE predefined variable Use the CI_PIPELINE_SOURCE variable to control when to add jobs for these pipeline types: Value Description api For pipelines triggered by the pipelines API . chat For pipelines created by using a GitLab ChatOps command. external When you use CI services other than GitLab. external_pull_request_event When an external pull request on GitHub is created or updated. merge_request_event For pipelines created when a merge request is created or updated. Required to enable merge request pipelines , merged results pipelines , and merge trains . ondemand_dast_scan For DAST on-demand scan pipelines. ondemand_dast_validation For DAST on-demand validation pipelines parent_pipeline For pipelines triggered by a parent/child pipeline . Use this pipeline source in the child pipeline configuration so that it can be triggered by the parent pipeline. pipeline For multi-project pipelines . push For pipelines triggered by a Git push event, including for branches and tags. schedule For scheduled pipelines . security_orchestration_policy For scheduled scan execution policies pipelines. trigger For pipelines created by using a trigger token . web For pipelines created by selecting New pipeline in the GitLab UI, from the project’s Build > Pipelines section. webide For pipelines created by using the Web IDE . These values are the same as returned for the source parameter when using the pipelines API endpoint . Complex rules You can use all rules keywords, like if , changes , and exists , in the same rule. The rule evaluates to true only when all included keywords evaluate to true. For example: yaml docker build : script : docker build -t my-image:$CI_COMMIT_REF_SLUG . rules : - if : $VAR == "string value" changes : # Include the job and set to when:manual if any of the follow paths match a modified file. - Dockerfile - docker/scripts/**/* when : manual allow_failure : true If the Dockerfile file or any file in /docker/scripts has changed and $VAR == "string value" , then the job runs manually and is allowed to fail. You can use parentheses with && and || to build more complicated variable expressions. yaml job1 : script : - echo This rule uses parentheses. rules : - if : ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE Avoid duplicate pipelines If a job uses rules , a single action, like pushing a commit to a branch, can trigger multiple pipelines. You don’t have to explicitly configure rules for multiple types of pipeline to trigger them accidentally. For example: yaml job : script : echo "This job creates double pipelines!" rules : - if : $CUSTOM_VARIABLE == "false" when : never - when : always This job does not run when $CUSTOM_VARIABLE is false, but it does run in all other pipelines, including both push (branch) and merge request pipelines. With this configuration, every push to an open merge request’s source branch causes duplicated pipelines. To avoid duplicate pipelines, you can: Use workflow to specify which types of pipelines can run. Rewrite the rules to run the job only in very specific cases, and avoid a final when rule: yaml job : script : echo "This job does NOT create double pipelines!" rules : - if : $CUSTOM_VARIABLE == "true" && $CI_PIPELINE_SOURCE == "merge_request_event" You can also avoid duplicate pipelines by changing the job rules to avoid either push (branch) pipelines or merge request pipelines. However, if you use a - when: always rule without workflow: rules , GitLab displays a pipeline warning . For example, the following does not trigger double pipelines, but is not recommended without workflow: rules : yaml job : script : echo "This job does NOT create double pipelines!" rules : - if : $CI_PIPELINE_SOURCE == "push" when : never - when : always You should not include both push and merge request pipelines in the same job without workflow:rules that prevent duplicate pipelines : yaml job : script : echo "This job creates double pipelines!" rules : - if : $CI_PIPELINE_SOURCE == "push" - if : $CI_PIPELINE_SOURCE == "merge_request_event" Also, do not mix only/except jobs with rules jobs in the same pipeline. It may not cause YAML errors, but the different default behaviors of only/except and rules can cause issues that are difficult to troubleshoot: yaml job-with-no-rules : script : echo "This job runs in branch pipelines." job-with-rules : script : echo "This job runs in merge request pipelines." rules : - if : $CI_PIPELINE_SOURCE == "merge_request_event" For every change pushed to the branch with an open merge request, duplicate pipelines run. One branch pipeline runs a single job ( job-with-no-rules ), and one merge request pipeline runs the other job ( job-with-rules ). Jobs with no rules default to except: merge_requests , so job-with-no-rules runs in all cases except merge requests. Reuse rules in different jobs Use !reference tags to reuse rules in different jobs. You can combine !reference rules with rules defined in the job. For example: yaml .default_rules : rules : - if : $CI_PIPELINE_SOURCE == "schedule" when : never - if : $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH job1 : rules : - ! reference [.default_rules, rules] script : - echo "This job runs for the default branch, but not schedules." job2 : rules : - ! reference [.default_rules, rules] - if : $CI_PIPELINE_SOURCE == "merge_request_event" script : - echo "This job runs for the default branch, but not schedules." - echo "It also runs for merge requests." CI/CD variable expressions Use variable expressions with rules:if to control when jobs should be added to a pipeline. You can use the equality operators == and != to compare a variable with a string. Both single quotes and double quotes are valid. The variable has to be on the left side of the comparison. For example: if: $VARIABLE == "some value" if: $VARIABLE != "some value" You can compare the values of two variables. For example: if: $VARIABLE_1 == $VARIABLE_2 if: $VARIABLE_1 != $VARIABLE_2 You can compare a variable to the null keyword to see if it is defined. For example: if: $VARIABLE == null if: $VARIABLE != null You can check if a variable is defined but empty. For example: if: $VARIABLE == "" if: $VARIABLE != "" You can check if a variable is both defined and not empty by using just the variable name in the expression. For example: if: $VARIABLE You can also use CI/CD inputs in variable expressions . Compare a variable to a regular expression You can do regular expression matching on variable values with the =~ and !~ operators. Expressions evaluate as true if: Matches are found when using =~ . Matches are not found when using !~ . For example: if: $VARIABLE =~ /^content.*/ if: $VARIABLE !~ /^content.*/ Additionally: Single-character regular expressions, like /./ , are not supported and produce an invalid expression syntax error. Pattern matching is case-sensitive by default. Use the i flag modifier to make a pattern case-insensitive. For example: /pattern/i . Only the tag or branch name can be matched by a regular expression. The repository path, if given, is always matched literally. The entire pattern must be surrounded by / . For example, you can’t use issue-/.*/ to match all tag names or branch names that begin with issue- , but you can use /issue-.*/ . The @ symbol denotes the beginning of a ref’s repository path. To match a ref name that contains the @ character in a regular expression, you must use the hex character code match \x40 . Use anchors ^ and $ to avoid the regular expression matching only a substring of the tag name or branch name. For example, /^issue-.*$/ is equivalent to /^issue-/ , while just /issue/ would also match a branch called severe-issues . Variable pattern matching with regular expressions uses the RE2 regular expression syntax . Store a regular expression in a variable Variables on the right side of =~ and !~ expressions are evaluated as regular expressions. The regular expression must be enclosed in forward slashes ( / ). For example: yaml variables : pattern : '/^ab.*/' regex-job1 : variables : teststring : 'abcde' script : echo "This job will run, because 'abcde' matches the /^ab.*/ pattern." rules : - if : '$teststring =~ $pattern' regex-job2 : variables : teststring : 'fghij' script : echo "This job will not run, because 'fghi' does not match the /^ab.*/ pattern." rules : - if : '$teststring =~ $pattern' Variables in a regular expression are not expanded. For example: yaml variables : string1 : 'regex-job1' string2 : 'regex-job2' pattern : '/$string2/' regex-job1 : script : echo "This job will NOT run, because the 'string1' variable inside the regex pattern is not expanded." rules : - if : '$CI_JOB_NAME =~ /$string1/' regex-job2 : script : echo "This job will NOT run, because the 'string2' variable inside the 'pattern' variable is not expanded." rules : - if : '$CI_JOB_NAME =~ $pattern' Join variable expressions together You can join multiple expressions using && (and) or || (or), for example: $VARIABLE1 =~ /^content.*/ && $VARIABLE2 == "something" $VARIABLE1 =~ /^content.*/ && $VARIABLE2 =~ /thing$/ && $VARIABLE3 $VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/ && $VARIABLE3 You can use parentheses to group expressions together. Parentheses take precedence over && and || , so expressions enclosed in parentheses evaluate first, and the result is used for the rest of the expression. For the precedence of operators, && evaluates before || . Nest parentheses to create complex conditions, and the inner-most expressions in parentheses evaluate first. For example: ($VARIABLE1 =~ /^content.*/ || $VARIABLE2) && ($VARIABLE3 =~ /thing$/ || $VARIABLE4) ($VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/) && $VARIABLE3 $CI_COMMIT_BRANCH == "my-branch" || (($VARIABLE1 == "thing" || $VARIABLE2 == "thing") && $VARIABLE3) Migrate from only or except to rules Use rules and CI/CD variable expressions to reproduce the same behavior as the deprecated only and except keywords . For example, starting with this deprecated configuration: yaml job1 : script : echo only : - main - /^stable-branch.*$/ - schedules job2 : script : echo except : - main - /^issue-.*$/ - merge_requests In this example: job1 uses only to run in pipelines when: The branch is the default branch ( main ). The branch name matches the pattern /^stable-branch.*$/ . The pipeline runs on a schedule. job2 uses except to skip pipelines when: The branch is the default branch ( main ). The branch name matches the pattern /^issue-.*$/ . The pipeline is a merge request pipeline. To create similar pipeline configuration with rules , use CI/CD variable expressions. For example, for a direct migration from only and except to rules : yaml job1 : script : echo rules : - if : $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if : $CI_COMMIT_BRANCH =~ /^stable-branch.*$/ - if : $CI_PIPELINE_SOURCE == "schedule" job2 : script : echo rules : - if : $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when : never - if : $CI_PIPELINE_SOURCE == "merge_request_event" when : never - if : $CI_COMMIT_BRANCH =~ /^issue-.*$/ when : never - when : on_success Both jobs behave the same way with rules as with only and except . However, you can simplify job2 to avoid when: never rules. Define rules for when job2 should run instead of when it should not run. For example, if job2 should run for all branches except the default branch, and also for tags: yaml job2 : script : echo rules : - if : $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH - if : $CI_COMMIT_TAG In this example, job2 runs when the branch is not the default branch, and when a new Git tag is created. Otherwise, the job does not run. Troubleshooting Unexpected behavior from regular expression matching with =~ When using the =~ character, make sure the right side of the comparison always contains a valid regular expression. If the right side of the comparison is not a valid regular expression enclosed with / characters, the expression evaluates in an unexpected way. In that case, the comparison checks if the left side is a substring of the right side. For example, "23" =~ "1234" evaluates to true, which is the opposite of "23" =~ /1234/ , which evaluates to false. You should not configure your pipeline to rely on this behavior.
Markdown
[Skip to main content](https://docs.gitlab.com/ci/jobs/job_rules/#skipTarget) [Go to GitLab Docs homepage](https://docs.gitlab.com/) [What's new?](https://about.gitlab.com/releases/whats-new/) English - Language - English - 日本語 v18.10 - - [18\.10 (not yet released)](https://docs.gitlab.com/ci/jobs/job_rules/) - - [18\.9 (recently released)](https://docs.gitlab.com/18.9/ci/jobs/job_rules/) - [18\.8](https://docs.gitlab.com/18.8/ci/jobs/job_rules/) - [18\.7](https://docs.gitlab.com/18.7/ci/jobs/job_rules/) - - [17\.11](https://docs.gitlab.com/17.11/ci/jobs/job_rules/) - [16\.11](https://docs.gitlab.com/16.11/ee/ci/jobs/job_rules.html) - - [Archives](https://docs.gitlab.com/archives) Select theme and layout - Light mode - Dark mode - Auto - Fixed width - Fluid width [What's new?](https://about.gitlab.com/releases/whats-new/) [Get free trial](https://gitlab.com/-/trial_registrations/new?glm_source=docs.gitlab.com&glm_content=navigation-cta-docs) Toggle menu - [Use GitLab](https://docs.gitlab.com/user/) - [GitLab Duo](https://docs.gitlab.com/user/gitlab_duo/) - [Extend](https://docs.gitlab.com/api/) - [Install](https://docs.gitlab.com/install/) - [Administer](https://docs.gitlab.com/administration/) - [Subscribe](https://docs.gitlab.com/subscriptions/) - [Contribute](https://docs.gitlab.com/development/) - [Solutions](https://docs.gitlab.com/solutions/) Select a topic [Getting started](https://docs.gitlab.com/user/get_started/) [Tutorials](https://docs.gitlab.com/tutorials/) [Manage your organization](https://docs.gitlab.com/topics/set_up_organization/) [Organize work with projects](https://docs.gitlab.com/user/project/organize_work_with_projects/) [Plan and track work](https://docs.gitlab.com/topics/plan_and_track/) [Manage authentication and authorization](https://docs.gitlab.com/auth/) [Use Git](https://docs.gitlab.com/topics/git/) [Manage your code](https://docs.gitlab.com/topics/manage_code/) [Use CI/CD to build your application](https://docs.gitlab.com/topics/build_your_application/) [Getting started](https://docs.gitlab.com/ci/) [Tutorials](https://docs.gitlab.com/tutorials/build_application/) [CI/CD YAML syntax reference](https://docs.gitlab.com/ci/yaml/) [Runners](https://docs.gitlab.com/ci/runners/) [Pipelines](https://docs.gitlab.com/ci/pipelines/) [Jobs](https://docs.gitlab.com/ci/jobs/) [Control how jobs run](https://docs.gitlab.com/ci/jobs/job_control/) [Specify when jobs run with rules](https://docs.gitlab.com/ci/jobs/job_rules/) [Troubleshooting](https://docs.gitlab.com/ci/jobs/job_troubleshooting/) [Job inputs](https://docs.gitlab.com/ci/jobs/job_inputs/) [Format scripts and job logs](https://docs.gitlab.com/ci/yaml/script/) [Job execution flow](https://docs.gitlab.com/ci/jobs/job_execution/) [Caching](https://docs.gitlab.com/ci/caching/) [Artifacts](https://docs.gitlab.com/ci/jobs/job_artifacts/) [SSH keys](https://docs.gitlab.com/ci/jobs/ssh_keys/) [Docker](https://docs.gitlab.com/ci/docker/) [Services](https://docs.gitlab.com/ci/services/) [Git submodules](https://docs.gitlab.com/ci/runners/git_submodules/) [Access a terminal for a running job](https://docs.gitlab.com/ci/interactive_web_terminal/) [CI/CD job logs](https://docs.gitlab.com/ci/jobs/job_logs/) [CI/CD components](https://docs.gitlab.com/ci/components/) [Variables](https://docs.gitlab.com/ci/variables/) [Pipeline security](https://docs.gitlab.com/ci/pipeline_security/) [External secrets](https://docs.gitlab.com/ci/secrets/) [Debugging](https://docs.gitlab.com/ci/debugging/) [Auto DevOps](https://docs.gitlab.com/topics/autodevops/) [Testing](https://docs.gitlab.com/ci/testing/) [Google Cloud integration](https://docs.gitlab.com/ci/gitlab_google_cloud_integration/) [Migrate to GitLab CI/CD](https://docs.gitlab.com/ci/migration/plan_a_migration/) [External repository integrations](https://docs.gitlab.com/ci/ci_cd_for_external_repos/) [Mobile DevOps](https://docs.gitlab.com/ci/mobile_devops/) [Secure your application](https://docs.gitlab.com/user/application_security/secure_your_application/) [Deploy and release your application](https://docs.gitlab.com/topics/release_your_application/) [Manage your infrastructure](https://docs.gitlab.com/user/infrastructure/) [Monitor your application](https://docs.gitlab.com/operations/) [Analyze GitLab usage](https://docs.gitlab.com/user/analytics/) [Feature support](https://docs.gitlab.com/policy/development_stages_support/) [Find your GitLab version](https://docs.gitlab.com/user/version/) / 1. Show more breadcrumbs - [GitLab Docs](https://docs.gitlab.com/) - [Use GitLab](https://docs.gitlab.com/user/) 2. [Use CI/CD to build your …](https://docs.gitlab.com/topics/build_your_application/) 3. [Jobs](https://docs.gitlab.com/ci/jobs/) 4. [Control how jobs run](https://docs.gitlab.com/ci/jobs/job_control/) 5. [Specify when jobs run with rules](https://docs.gitlab.com/ci/jobs/job_rules/) *** # Specify when jobs run with `rules` - Tier: Free, Premium, Ultimate - Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated Use the [`rules`](https://docs.gitlab.com/ci/yaml/#rules) keyword to include or exclude jobs in pipelines. Rules are evaluated in order until the first match. When a match is found, the job is either included or excluded from the pipeline, depending on the configuration. You cannot use dotenv variables created in job scripts in rules, because rules are evaluated before any jobs run. ## `rules` examples The following example uses `if` to define that the job runs in only two specific cases: yaml ``` job: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual allow_failure: true - if: $CI_PIPELINE_SOURCE == "schedule" ``` - If the pipeline is for a merge request, the first rule matches, and the job is added to the merge request pipeline with attributes of: - `when: manual` (manual job) - `allow_failure: true` (the pipeline continues running even if the manual job is not run) - If the pipeline is not for a merge request, the first rule doesn’t match, and the second rule is evaluated. - If the pipeline is a scheduled pipeline, the second rule matches, and the job is added to the scheduled pipeline. No attributes were defined, so it is added with: - `when: on_success` (default) - `allow_failure: false` (default) - In all other cases, no rules match, so the job is not added to any other pipeline. Alternatively, you can define a set of rules to exclude jobs in a few cases, but run them in all other cases: yaml ``` job: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: never - if: $CI_PIPELINE_SOURCE == "schedule" when: never - when: on_success ``` - If the pipeline is for a merge request, the job is not added to the pipeline. - If the pipeline is a scheduled pipeline, the job is not added to the pipeline. - In all other cases, the job is added to the pipeline, with `when: on_success`. If you use a `when` clause as the final rule (not including `when: never`), two simultaneous pipelines may start. Both push pipelines and merge request pipelines can be triggered by the same event (a push to the source branch for an open merge request). See how to [avoid duplicate pipelines](https://docs.gitlab.com/ci/jobs/job_rules/#avoid-duplicate-pipelines) for more details. ### Run jobs for scheduled pipelines You can configure a job to be executed only when the pipeline has been scheduled. For example: yaml ``` job:on-schedule: rules: - if: $CI_PIPELINE_SOURCE == "schedule" script: - make world job: rules: - if: $CI_PIPELINE_SOURCE == "push" script: - make build ``` In this example, `make world` runs in scheduled pipelines, and `make build` runs in branch and tag pipelines. ### Skip jobs if the branch is empty Use [`rules:changes:compare_to`](https://docs.gitlab.com/ci/yaml/#ruleschangescompare_to) to skip a job when the branch is empty, which saves CI/CD resources. The configuration compares the branch to the default branch, and if the branch: - Doesn’t have changed files, the job doesn’t run. - Has changed files, the job runs. For example, in a project with `main` as the default branch: yaml ``` job: script: - echo "This job only runs for branches that are not empty" rules: - if: $CI_COMMIT_BRANCH changes: compare_to: 'refs/heads/main' paths: - '**/*' ``` The rule for this job compares all files and paths in the current branch recursively (`**/*`) against the `main` branch. The rule matches and the job runs only when there are changes to the files in the branch. ## Run a job when a file is not present You can use `rules: exists` to configure a job to run only when a specific file does not exist. For example, to run a job in a merge request pipeline when the `example.yml` file does not exist: yaml ``` job: script: echo "Hello, Rules!" rules: - exists: - "example_dir/example.yml" when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" ``` In this example, if the `example_dir/example.yml` file exists in the branch, the job does not run. If the file does not exist, the job can run in merge request pipelines. ## Common `if` clauses with predefined variables `rules:if` clauses are commonly used with [predefined CI/CD variables](https://docs.gitlab.com/ci/variables/predefined_variables/), especially `CI_PIPELINE_SOURCE`. The following example runs the job as a manual job in scheduled pipelines or in push pipelines (to branches or tags), with `when: on_success` (default). It does not add the job to any other pipeline type. yaml ``` job: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "schedule" when: manual allow_failure: true - if: $CI_PIPELINE_SOURCE == "push" ``` The following example runs the job as a `when: on_success` job in merge request pipelines and scheduled pipelines. It does not run in any other pipeline type. yaml ``` job: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "schedule" ``` Other commonly used `if` clauses: - `if: $CI_COMMIT_TAG`: If changes are pushed for a tag. - `if: $CI_COMMIT_BRANCH`: If changes are pushed to any branch. - `if: $CI_COMMIT_BRANCH == "main"`: If changes are pushed to `main`. - `if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH`: If changes are pushed to the default branch. - `if: $CI_COMMIT_BRANCH =~ /regex-expression/`: If the commit branch matches a regular expression. - `if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_TITLE =~ /Merge branch.*/`: If the commit branch is the default branch and the commit message title matches a regular expression. - `if: $CUSTOM_VARIABLE == "value1"`: If the custom variable `CUSTOM_VARIABLE` is exactly `value1`. ### Run jobs only in specific pipeline types You can use predefined CI/CD variables with `rules` to choose which pipeline types jobs should run for. The following table lists some of the variables that you can use, and the pipeline types the variables can control for: - Branch pipelines that run for Git `push` events to a branch, like new commits or tags. - Tag pipelines that run only when a new Git tag is pushed to a branch. - Merge request pipelines that run for changes to a merge request, like new commits or selecting **Run pipeline** in a merge request’s pipelines tab. - Scheduled pipelines. | Variables | Branch | Tag | Merge request | Scheduled | |---|---|---|---|---| | `CI_COMMIT_BRANCH` | Yes | | | Yes | | `CI_COMMIT_TAG` | | Yes | | Yes, if the scheduled pipeline is configured to run on a tag. | | `CI_PIPELINE_SOURCE = push` | Yes | Yes | | | | `CI_PIPELINE_SOURCE = schedule` | | | | Yes | | `CI_PIPELINE_SOURCE = merge_request_event` | | | Yes | | | `CI_MERGE_REQUEST_IID` | | | Yes | | For example, to configure a job to run for merge request pipelines and scheduled pipelines, but not branch or tag pipelines: yaml ``` job1: script: - echo rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_PIPELINE_SOURCE == "push" when: never ``` ### `CI_PIPELINE_SOURCE` predefined variable Use the `CI_PIPELINE_SOURCE` variable to control when to add jobs for these pipeline types: | Value | Description | |---|---| | `api` | For pipelines triggered by the [pipelines API](https://docs.gitlab.com/api/pipelines/#create-a-new-pipeline). | | `chat` | For pipelines created by using a [GitLab ChatOps](https://docs.gitlab.com/ci/chatops/) command. | | `external` | When you use CI services other than GitLab. | | `external_pull_request_event` | When an [external pull request on GitHub](https://docs.gitlab.com/ci/ci_cd_for_external_repos/#pipelines-for-external-pull-requests) is created or updated. | | `merge_request_event` | For pipelines created when a merge request is created or updated. Required to enable [merge request pipelines](https://docs.gitlab.com/ci/pipelines/merge_request_pipelines/), [merged results pipelines](https://docs.gitlab.com/ci/pipelines/merged_results_pipelines/), and [merge trains](https://docs.gitlab.com/ci/pipelines/merge_trains/). | | `ondemand_dast_scan` | For [DAST on-demand scan](https://docs.gitlab.com/user/application_security/dast/on-demand_scan/) pipelines. | | `ondemand_dast_validation` | For [DAST on-demand validation](https://docs.gitlab.com/user/application_security/dast/profiles/#site-profile-validation) pipelines | | `parent_pipeline` | For pipelines triggered by a [parent/child pipeline](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#parent-child-pipelines). Use this pipeline source in the child pipeline configuration so that it can be triggered by the parent pipeline. | | `pipeline` | For [multi-project pipelines](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#multi-project-pipelines). | | `push` | For pipelines triggered by a Git push event, including for branches and tags. | | `schedule` | For [scheduled pipelines](https://docs.gitlab.com/ci/pipelines/schedules/). | | `security_orchestration_policy` | For [scheduled scan execution policies](https://docs.gitlab.com/user/application_security/policies/scan_execution_policies/) pipelines. | | `trigger` | For pipelines created by using a [trigger token](https://docs.gitlab.com/ci/triggers/#configure-cicd-jobs-to-run-in-triggered-pipelines). | | `web` | For pipelines created by selecting **New pipeline** in the GitLab UI, from the project’s **Build** \> **Pipelines** section. | | `webide` | For pipelines created by using the [Web IDE](https://docs.gitlab.com/user/project/web_ide/). | These values are the same as returned for the `source` parameter when using the [pipelines API endpoint](https://docs.gitlab.com/api/pipelines/#list-project-pipelines). ## Complex rules You can use all `rules` keywords, like `if`, `changes`, and `exists`, in the same rule. The rule evaluates to true only when all included keywords evaluate to true. For example: yaml ``` docker build: script: docker build -t my-image:$CI_COMMIT_REF_SLUG . rules: - if: $VAR == "string value" changes: # Include the job and set to when:manual if any of the follow paths match a modified file. - Dockerfile - docker/scripts/**/* when: manual allow_failure: true ``` If the `Dockerfile` file or any file in `/docker/scripts` has changed and `$VAR == "string value"`, then the job runs manually and is allowed to fail. You can use parentheses with `&&` and `||` to build more complicated variable expressions. yaml ``` job1: script: - echo This rule uses parentheses. rules: - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE ``` ## Avoid duplicate pipelines If a job uses `rules`, a single action, like pushing a commit to a branch, can trigger multiple pipelines. You don’t have to explicitly configure rules for multiple types of pipeline to trigger them accidentally. For example: yaml ``` job: script: echo "This job creates double pipelines!" rules: - if: $CUSTOM_VARIABLE == "false" when: never - when: always ``` This job does not run when `$CUSTOM_VARIABLE` is false, but it does run in all other pipelines, including **both** push (branch) and merge request pipelines. With this configuration, every push to an open merge request’s source branch causes duplicated pipelines. To avoid duplicate pipelines, you can: - Use [`workflow`](https://docs.gitlab.com/ci/yaml/#workflow) to specify which types of pipelines can run. - Rewrite the rules to run the job only in very specific cases, and avoid a final `when` rule: yaml ``` job: script: echo "This job does NOT create double pipelines!" rules: - if: $CUSTOM_VARIABLE == "true" && $CI_PIPELINE_SOURCE == "merge_request_event" ``` You can also avoid duplicate pipelines by changing the job rules to avoid either push (branch) pipelines or merge request pipelines. However, if you use a `- when: always` rule without `workflow: rules`, GitLab displays a [pipeline warning](https://docs.gitlab.com/ci/debugging/#pipeline-warnings). For example, the following does not trigger double pipelines, but is not recommended without `workflow: rules`: yaml ``` job: script: echo "This job does NOT create double pipelines!" rules: - if: $CI_PIPELINE_SOURCE == "push" when: never - when: always ``` You should not include both push and merge request pipelines in the same job without [`workflow:rules` that prevent duplicate pipelines](https://docs.gitlab.com/ci/yaml/workflow/#switch-between-branch-pipelines-and-merge-request-pipelines): yaml ``` job: script: echo "This job creates double pipelines!" rules: - if: $CI_PIPELINE_SOURCE == "push" - if: $CI_PIPELINE_SOURCE == "merge_request_event" ``` Also, do not mix `only/except` jobs with `rules` jobs in the same pipeline. It may not cause YAML errors, but the different default behaviors of `only/except` and `rules` can cause issues that are difficult to troubleshoot: yaml ``` job-with-no-rules: script: echo "This job runs in branch pipelines." job-with-rules: script: echo "This job runs in merge request pipelines." rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" ``` For every change pushed to the branch with an open merge request, duplicate pipelines run. One branch pipeline runs a single job (`job-with-no-rules`), and one merge request pipeline runs the other job (`job-with-rules`). Jobs with no rules default to [`except: merge_requests`](https://docs.gitlab.com/ci/yaml/deprecated_keywords/#only--except), so `job-with-no-rules` runs in all cases except merge requests. ## Reuse rules in different jobs Use [`!reference` tags](https://docs.gitlab.com/ci/yaml/yaml_optimization/#reference-tags) to reuse rules in different jobs. You can combine `!reference` rules with rules defined in the job. For example: yaml ``` .default_rules: rules: - if: $CI_PIPELINE_SOURCE == "schedule" when: never - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH job1: rules: - !reference [.default_rules, rules] script: - echo "This job runs for the default branch, but not schedules." job2: rules: - !reference [.default_rules, rules] - if: $CI_PIPELINE_SOURCE == "merge_request_event" script: - echo "This job runs for the default branch, but not schedules." - echo "It also runs for merge requests." ``` ## CI/CD variable expressions Use variable expressions with [`rules:if`](https://docs.gitlab.com/ci/yaml/#rulesif) to control when jobs should be added to a pipeline. You can use the equality operators `==` and `!=` to compare a variable with a string. Both single quotes and double quotes are valid. The variable has to be on the left side of the comparison. For example: - `if: $VARIABLE == "some value"` - `if: $VARIABLE != "some value"` You can compare the values of two variables. For example: - `if: $VARIABLE_1 == $VARIABLE_2` - `if: $VARIABLE_1 != $VARIABLE_2` You can compare a variable to the `null` keyword to see if it is defined. For example: - `if: $VARIABLE == null` - `if: $VARIABLE != null` You can check if a variable is defined but empty. For example: - `if: $VARIABLE == ""` - `if: $VARIABLE != ""` You can check if a variable is both defined and not empty by using just the variable name in the expression. For example: - `if: $VARIABLE` You can also [use CI/CD inputs in variable expressions](https://docs.gitlab.com/ci/inputs/examples/#use-cicd-inputs-in-variable-expressions). ### Compare a variable to a regular expression You can do regular expression matching on variable values with the `=~` and `!~` operators. Expressions evaluate as `true` if: - Matches are found when using `=~`. - Matches are not found when using `!~`. For example: - `if: $VARIABLE =~ /^content.*/` - `if: $VARIABLE !~ /^content.*/` Additionally: - Single-character regular expressions, like `/./`, are not supported and produce an `invalid expression syntax` error. - Pattern matching is case-sensitive by default. Use the `i` flag modifier to make a pattern case-insensitive. For example: `/pattern/i`. - Only the tag or branch name can be matched by a regular expression. The repository path, if given, is always matched literally. - The entire pattern must be surrounded by `/`. For example, you can’t use `issue-/.*/` to match all tag names or branch names that begin with `issue-`, but you can use `/issue-.*/`. - The `@` symbol denotes the beginning of a ref’s repository path. To match a ref name that contains the `@` character in a regular expression, you must use the hex character code match `\x40`. - Use anchors `^` and `$` to avoid the regular expression matching only a substring of the tag name or branch name. For example, `/^issue-.*$/` is equivalent to `/^issue-/`, while just `/issue/` would also match a branch called `severe-issues`. - Variable pattern matching with regular expressions uses the [RE2 regular expression syntax](https://github.com/google/re2/wiki/Syntax). ### Store a regular expression in a variable Variables on the right side of `=~` and `!~` expressions are evaluated as regular expressions. The regular expression must be enclosed in forward slashes (`/`). For example: yaml ``` variables: pattern: '/^ab.*/' regex-job1: variables: teststring: 'abcde' script: echo "This job will run, because 'abcde' matches the /^ab.*/ pattern." rules: - if: '$teststring =~ $pattern' regex-job2: variables: teststring: 'fghij' script: echo "This job will not run, because 'fghi' does not match the /^ab.*/ pattern." rules: - if: '$teststring =~ $pattern' ``` Variables in a regular expression are not expanded. For example: yaml ``` variables: string1: 'regex-job1' string2: 'regex-job2' pattern: '/$string2/' regex-job1: script: echo "This job will NOT run, because the 'string1' variable inside the regex pattern is not expanded." rules: - if: '$CI_JOB_NAME =~ /$string1/' regex-job2: script: echo "This job will NOT run, because the 'string2' variable inside the 'pattern' variable is not expanded." rules: - if: '$CI_JOB_NAME =~ $pattern' ``` ### Join variable expressions together You can join multiple expressions using `&&` (and) or `||` (or), for example: - `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 == "something"` - `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 =~ /thing$/ && $VARIABLE3` - `$VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/ && $VARIABLE3` You can use parentheses to group expressions together. Parentheses take precedence over `&&` and `||`, so expressions enclosed in parentheses evaluate first, and the result is used for the rest of the expression. For the precedence of operators, `&&` evaluates before `||`. Nest parentheses to create complex conditions, and the inner-most expressions in parentheses evaluate first. For example: - `($VARIABLE1 =~ /^content.*/ || $VARIABLE2) && ($VARIABLE3 =~ /thing$/ || $VARIABLE4)` - `($VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/) && $VARIABLE3` - `$CI_COMMIT_BRANCH == "my-branch" || (($VARIABLE1 == "thing" || $VARIABLE2 == "thing") && $VARIABLE3)` ## Migrate from `only` or `except` to `rules` Use `rules` and CI/CD variable expressions to reproduce the same behavior as the deprecated [`only` and `except` keywords](https://docs.gitlab.com/ci/yaml/deprecated_keywords/#only--except). For example, starting with this deprecated configuration: yaml ``` job1: script: echo only: - main - /^stable-branch.*$/ - schedules job2: script: echo except: - main - /^issue-.*$/ - merge_requests ``` In this example: - `job1` uses `only` to run in pipelines when: - The branch is the default branch (`main`). - The branch name matches the pattern `/^stable-branch.*$/`. - The pipeline runs on a schedule. - `job2` uses `except` to skip pipelines when: - The branch is the default branch (`main`). - The branch name matches the pattern `/^issue-.*$/`. - The pipeline is a merge request pipeline. To create similar pipeline configuration with `rules`, use CI/CD variable expressions. For example, for a direct migration from `only` and `except` to `rules`: yaml ``` job1: script: echo rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH =~ /^stable-branch.*$/ - if: $CI_PIPELINE_SOURCE == "schedule" job2: script: echo rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: never - if: $CI_COMMIT_BRANCH =~ /^issue-.*$/ when: never - when: on_success ``` Both jobs behave the same way with `rules` as with `only` and `except`. However, you can simplify `job2` to avoid `when: never` rules. Define rules for when `job2` should run instead of when it should not run. For example, if `job2` should run for all branches except the default branch, and also for tags: yaml ``` job2: script: echo rules: - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAG ``` In this example, `job2` runs when the branch is not the default branch, and when a new Git tag is created. Otherwise, the job does not run. ## Troubleshooting ### Unexpected behavior from regular expression matching with `=~` When using the `=~` character, make sure the right side of the comparison always contains a valid regular expression. If the right side of the comparison is not a valid regular expression enclosed with `/` characters, the expression evaluates in an unexpected way. In that case, the comparison checks if the left side is a substring of the right side. For example, `"23" =~ "1234"` evaluates to true, which is the opposite of `"23" =~ /1234/`, which evaluates to false. You should not configure your pipeline to rely on this behavior. Was this page helpful? Yes No Edit this page - - [Open in Web IDE`.`Quickly and easily edit multiple files.](https://gitlab.com/-/ide/project/gitlab-org/gitlab/edit/master/-/doc/ci/jobs/job_rules.md) - [View page sourceEdit this file only.](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/ci/jobs/job_rules.md) - [Create an issueSuggest improvements.](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Documentation) - [rules examples](https://docs.gitlab.com/ci/jobs/job_rules/#rules-examples) - [Run jobs for scheduled pipelines](https://docs.gitlab.com/ci/jobs/job_rules/#run-jobs-for-scheduled-pipelines) - [Skip jobs if the branch is empty](https://docs.gitlab.com/ci/jobs/job_rules/#skip-jobs-if-the-branch-is-empty) - [Run a job when a file is not present](https://docs.gitlab.com/ci/jobs/job_rules/#run-a-job-when-a-file-is-not-present) - [Common if clauses with predefined variables](https://docs.gitlab.com/ci/jobs/job_rules/#common-if-clauses-with-predefined-variables) - [Run jobs only in specific pipeline types](https://docs.gitlab.com/ci/jobs/job_rules/#run-jobs-only-in-specific-pipeline-types) - [CI\_PIPELINE\_SOURCE predefined variable](https://docs.gitlab.com/ci/jobs/job_rules/#ci_pipeline_source-predefined-variable) - [Complex rules](https://docs.gitlab.com/ci/jobs/job_rules/#complex-rules) - [Avoid duplicate pipelines](https://docs.gitlab.com/ci/jobs/job_rules/#avoid-duplicate-pipelines) - [Reuse rules in different jobs](https://docs.gitlab.com/ci/jobs/job_rules/#reuse-rules-in-different-jobs) - [CI/CD variable expressions](https://docs.gitlab.com/ci/jobs/job_rules/#cicd-variable-expressions) - [Compare a variable to a regular expression](https://docs.gitlab.com/ci/jobs/job_rules/#compare-a-variable-to-a-regular-expression) - [Store a regular expression in a variable](https://docs.gitlab.com/ci/jobs/job_rules/#store-a-regular-expression-in-a-variable) - [Join variable expressions together](https://docs.gitlab.com/ci/jobs/job_rules/#join-variable-expressions-together) - [Migrate from only or except to rules](https://docs.gitlab.com/ci/jobs/job_rules/#migrate-from-only-or-except-to-rules) - [Troubleshooting](https://docs.gitlab.com/ci/jobs/job_rules/#troubleshooting) - [Unexpected behavior from regular expression matching with =~](https://docs.gitlab.com/ci/jobs/job_rules/#unexpected-behavior-from-regular-expression-matching-with-) [![GitLab Docs logo](https://docs.gitlab.com/gitlab-logo-footer.svg)](https://docs.gitlab.com/) - [Facebook](https://www.facebook.com/gitlab) - [LinkedIn](https://www.linkedin.com/company/gitlab-com) - [Twitter](https://twitter.com/gitlab) - [YouTube](https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg) [![Creative Commons License](https://docs.gitlab.com/by-sa.svg)](https://creativecommons.org/licenses/by-sa/4.0/) Company - [About GitLab](https://about.gitlab.com/company/) - [View pricing](https://about.gitlab.com/pricing/) - [Try GitLab for free](https://about.gitlab.com/free-trial/) Feedback - [View page source](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/ci/jobs/job_rules.md) - [Edit in Web IDE](https://gitlab.com/-/ide/project/gitlab-org/gitlab/edit/master/-/doc/ci/jobs/job_rules.md) - [Contribute to GitLab](https://about.gitlab.com/community/contribute/) - [Suggest updates](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Documentation) Help & Community - [Get certified](https://university.gitlab.com/pages/certifications) - [Get support](https://about.gitlab.com/support/) - [Post on the GitLab forum](https://forum.gitlab.com/new-topic?title=topic%20title&body=topic%20body&tags=docs-feedback) Resources - [Terms](https://about.gitlab.com/terms/) - [Privacy statement](https://about.gitlab.com/privacy/) - [Use of generative AI](https://docs.gitlab.com/legal/use_generative_ai/) - [Acceptable use of user licenses](https://docs.gitlab.com/legal/licensing_policy/) ![](https://dc.ads.linkedin.com/collect/?pid=30694&fmt=gif)
Readable Markdown
- Tier: Free, Premium, Ultimate - Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated Use the [`rules`](https://docs.gitlab.com/ci/yaml/#rules) keyword to include or exclude jobs in pipelines. Rules are evaluated in order until the first match. When a match is found, the job is either included or excluded from the pipeline, depending on the configuration. You cannot use dotenv variables created in job scripts in rules, because rules are evaluated before any jobs run. ## `rules` examples The following example uses `if` to define that the job runs in only two specific cases: yaml ``` job: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual allow_failure: true - if: $CI_PIPELINE_SOURCE == "schedule" ``` - If the pipeline is for a merge request, the first rule matches, and the job is added to the merge request pipeline with attributes of: - `when: manual` (manual job) - `allow_failure: true` (the pipeline continues running even if the manual job is not run) - If the pipeline is not for a merge request, the first rule doesn’t match, and the second rule is evaluated. - If the pipeline is a scheduled pipeline, the second rule matches, and the job is added to the scheduled pipeline. No attributes were defined, so it is added with: - `when: on_success` (default) - `allow_failure: false` (default) - In all other cases, no rules match, so the job is not added to any other pipeline. Alternatively, you can define a set of rules to exclude jobs in a few cases, but run them in all other cases: yaml ``` job: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: never - if: $CI_PIPELINE_SOURCE == "schedule" when: never - when: on_success ``` - If the pipeline is for a merge request, the job is not added to the pipeline. - If the pipeline is a scheduled pipeline, the job is not added to the pipeline. - In all other cases, the job is added to the pipeline, with `when: on_success`. If you use a `when` clause as the final rule (not including `when: never`), two simultaneous pipelines may start. Both push pipelines and merge request pipelines can be triggered by the same event (a push to the source branch for an open merge request). See how to [avoid duplicate pipelines](https://docs.gitlab.com/ci/jobs/job_rules/#avoid-duplicate-pipelines) for more details. ### Run jobs for scheduled pipelines You can configure a job to be executed only when the pipeline has been scheduled. For example: yaml ``` job:on-schedule: rules: - if: $CI_PIPELINE_SOURCE == "schedule" script: - make world job: rules: - if: $CI_PIPELINE_SOURCE == "push" script: - make build ``` In this example, `make world` runs in scheduled pipelines, and `make build` runs in branch and tag pipelines. ### Skip jobs if the branch is empty Use [`rules:changes:compare_to`](https://docs.gitlab.com/ci/yaml/#ruleschangescompare_to) to skip a job when the branch is empty, which saves CI/CD resources. The configuration compares the branch to the default branch, and if the branch: - Doesn’t have changed files, the job doesn’t run. - Has changed files, the job runs. For example, in a project with `main` as the default branch: yaml ``` job: script: - echo "This job only runs for branches that are not empty" rules: - if: $CI_COMMIT_BRANCH changes: compare_to: 'refs/heads/main' paths: - '**/*' ``` The rule for this job compares all files and paths in the current branch recursively (`**/*`) against the `main` branch. The rule matches and the job runs only when there are changes to the files in the branch. ## Run a job when a file is not present You can use `rules: exists` to configure a job to run only when a specific file does not exist. For example, to run a job in a merge request pipeline when the `example.yml` file does not exist: yaml ``` job: script: echo "Hello, Rules!" rules: - exists: - "example_dir/example.yml" when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" ``` In this example, if the `example_dir/example.yml` file exists in the branch, the job does not run. If the file does not exist, the job can run in merge request pipelines. ## Common `if` clauses with predefined variables `rules:if` clauses are commonly used with [predefined CI/CD variables](https://docs.gitlab.com/ci/variables/predefined_variables/), especially `CI_PIPELINE_SOURCE`. The following example runs the job as a manual job in scheduled pipelines or in push pipelines (to branches or tags), with `when: on_success` (default). It does not add the job to any other pipeline type. yaml ``` job: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "schedule" when: manual allow_failure: true - if: $CI_PIPELINE_SOURCE == "push" ``` The following example runs the job as a `when: on_success` job in merge request pipelines and scheduled pipelines. It does not run in any other pipeline type. yaml ``` job: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "schedule" ``` Other commonly used `if` clauses: - `if: $CI_COMMIT_TAG`: If changes are pushed for a tag. - `if: $CI_COMMIT_BRANCH`: If changes are pushed to any branch. - `if: $CI_COMMIT_BRANCH == "main"`: If changes are pushed to `main`. - `if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH`: If changes are pushed to the default branch. - `if: $CI_COMMIT_BRANCH =~ /regex-expression/`: If the commit branch matches a regular expression. - `if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_TITLE =~ /Merge branch.*/`: If the commit branch is the default branch and the commit message title matches a regular expression. - `if: $CUSTOM_VARIABLE == "value1"`: If the custom variable `CUSTOM_VARIABLE` is exactly `value1`. ### Run jobs only in specific pipeline types You can use predefined CI/CD variables with `rules` to choose which pipeline types jobs should run for. The following table lists some of the variables that you can use, and the pipeline types the variables can control for: - Branch pipelines that run for Git `push` events to a branch, like new commits or tags. - Tag pipelines that run only when a new Git tag is pushed to a branch. - Merge request pipelines that run for changes to a merge request, like new commits or selecting **Run pipeline** in a merge request’s pipelines tab. - Scheduled pipelines. | Variables | Branch | Tag | Merge request | Scheduled | |---|---|---|---|---| | `CI_COMMIT_BRANCH` | Yes | | | Yes | | `CI_COMMIT_TAG` | | Yes | | Yes, if the scheduled pipeline is configured to run on a tag. | | `CI_PIPELINE_SOURCE = push` | Yes | Yes | | | | `CI_PIPELINE_SOURCE = schedule` | | | | Yes | | `CI_PIPELINE_SOURCE = merge_request_event` | | | Yes | | | `CI_MERGE_REQUEST_IID` | | | Yes | | For example, to configure a job to run for merge request pipelines and scheduled pipelines, but not branch or tag pipelines: yaml ``` job1: script: - echo rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_PIPELINE_SOURCE == "push" when: never ``` ### `CI_PIPELINE_SOURCE` predefined variable Use the `CI_PIPELINE_SOURCE` variable to control when to add jobs for these pipeline types: | Value | Description | |---|---| | `api` | For pipelines triggered by the [pipelines API](https://docs.gitlab.com/api/pipelines/#create-a-new-pipeline). | | `chat` | For pipelines created by using a [GitLab ChatOps](https://docs.gitlab.com/ci/chatops/) command. | | `external` | When you use CI services other than GitLab. | | `external_pull_request_event` | When an [external pull request on GitHub](https://docs.gitlab.com/ci/ci_cd_for_external_repos/#pipelines-for-external-pull-requests) is created or updated. | | `merge_request_event` | For pipelines created when a merge request is created or updated. Required to enable [merge request pipelines](https://docs.gitlab.com/ci/pipelines/merge_request_pipelines/), [merged results pipelines](https://docs.gitlab.com/ci/pipelines/merged_results_pipelines/), and [merge trains](https://docs.gitlab.com/ci/pipelines/merge_trains/). | | `ondemand_dast_scan` | For [DAST on-demand scan](https://docs.gitlab.com/user/application_security/dast/on-demand_scan/) pipelines. | | `ondemand_dast_validation` | For [DAST on-demand validation](https://docs.gitlab.com/user/application_security/dast/profiles/#site-profile-validation) pipelines | | `parent_pipeline` | For pipelines triggered by a [parent/child pipeline](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#parent-child-pipelines). Use this pipeline source in the child pipeline configuration so that it can be triggered by the parent pipeline. | | `pipeline` | For [multi-project pipelines](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#multi-project-pipelines). | | `push` | For pipelines triggered by a Git push event, including for branches and tags. | | `schedule` | For [scheduled pipelines](https://docs.gitlab.com/ci/pipelines/schedules/). | | `security_orchestration_policy` | For [scheduled scan execution policies](https://docs.gitlab.com/user/application_security/policies/scan_execution_policies/) pipelines. | | `trigger` | For pipelines created by using a [trigger token](https://docs.gitlab.com/ci/triggers/#configure-cicd-jobs-to-run-in-triggered-pipelines). | | `web` | For pipelines created by selecting **New pipeline** in the GitLab UI, from the project’s **Build** \> **Pipelines** section. | | `webide` | For pipelines created by using the [Web IDE](https://docs.gitlab.com/user/project/web_ide/). | These values are the same as returned for the `source` parameter when using the [pipelines API endpoint](https://docs.gitlab.com/api/pipelines/#list-project-pipelines). ## Complex rules You can use all `rules` keywords, like `if`, `changes`, and `exists`, in the same rule. The rule evaluates to true only when all included keywords evaluate to true. For example: yaml ``` docker build: script: docker build -t my-image:$CI_COMMIT_REF_SLUG . rules: - if: $VAR == "string value" changes: # Include the job and set to when:manual if any of the follow paths match a modified file. - Dockerfile - docker/scripts/**/* when: manual allow_failure: true ``` If the `Dockerfile` file or any file in `/docker/scripts` has changed and `$VAR == "string value"`, then the job runs manually and is allowed to fail. You can use parentheses with `&&` and `||` to build more complicated variable expressions. yaml ``` job1: script: - echo This rule uses parentheses. rules: - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE ``` ## Avoid duplicate pipelines If a job uses `rules`, a single action, like pushing a commit to a branch, can trigger multiple pipelines. You don’t have to explicitly configure rules for multiple types of pipeline to trigger them accidentally. For example: yaml ``` job: script: echo "This job creates double pipelines!" rules: - if: $CUSTOM_VARIABLE == "false" when: never - when: always ``` This job does not run when `$CUSTOM_VARIABLE` is false, but it does run in all other pipelines, including **both** push (branch) and merge request pipelines. With this configuration, every push to an open merge request’s source branch causes duplicated pipelines. To avoid duplicate pipelines, you can: - Use [`workflow`](https://docs.gitlab.com/ci/yaml/#workflow) to specify which types of pipelines can run. - Rewrite the rules to run the job only in very specific cases, and avoid a final `when` rule: yaml ``` job: script: echo "This job does NOT create double pipelines!" rules: - if: $CUSTOM_VARIABLE == "true" && $CI_PIPELINE_SOURCE == "merge_request_event" ``` You can also avoid duplicate pipelines by changing the job rules to avoid either push (branch) pipelines or merge request pipelines. However, if you use a `- when: always` rule without `workflow: rules`, GitLab displays a [pipeline warning](https://docs.gitlab.com/ci/debugging/#pipeline-warnings). For example, the following does not trigger double pipelines, but is not recommended without `workflow: rules`: yaml ``` job: script: echo "This job does NOT create double pipelines!" rules: - if: $CI_PIPELINE_SOURCE == "push" when: never - when: always ``` You should not include both push and merge request pipelines in the same job without [`workflow:rules` that prevent duplicate pipelines](https://docs.gitlab.com/ci/yaml/workflow/#switch-between-branch-pipelines-and-merge-request-pipelines): yaml ``` job: script: echo "This job creates double pipelines!" rules: - if: $CI_PIPELINE_SOURCE == "push" - if: $CI_PIPELINE_SOURCE == "merge_request_event" ``` Also, do not mix `only/except` jobs with `rules` jobs in the same pipeline. It may not cause YAML errors, but the different default behaviors of `only/except` and `rules` can cause issues that are difficult to troubleshoot: yaml ``` job-with-no-rules: script: echo "This job runs in branch pipelines." job-with-rules: script: echo "This job runs in merge request pipelines." rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" ``` For every change pushed to the branch with an open merge request, duplicate pipelines run. One branch pipeline runs a single job (`job-with-no-rules`), and one merge request pipeline runs the other job (`job-with-rules`). Jobs with no rules default to [`except: merge_requests`](https://docs.gitlab.com/ci/yaml/deprecated_keywords/#only--except), so `job-with-no-rules` runs in all cases except merge requests. ## Reuse rules in different jobs Use [`!reference` tags](https://docs.gitlab.com/ci/yaml/yaml_optimization/#reference-tags) to reuse rules in different jobs. You can combine `!reference` rules with rules defined in the job. For example: yaml ``` .default_rules: rules: - if: $CI_PIPELINE_SOURCE == "schedule" when: never - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH job1: rules: - !reference [.default_rules, rules] script: - echo "This job runs for the default branch, but not schedules." job2: rules: - !reference [.default_rules, rules] - if: $CI_PIPELINE_SOURCE == "merge_request_event" script: - echo "This job runs for the default branch, but not schedules." - echo "It also runs for merge requests." ``` ## CI/CD variable expressions Use variable expressions with [`rules:if`](https://docs.gitlab.com/ci/yaml/#rulesif) to control when jobs should be added to a pipeline. You can use the equality operators `==` and `!=` to compare a variable with a string. Both single quotes and double quotes are valid. The variable has to be on the left side of the comparison. For example: - `if: $VARIABLE == "some value"` - `if: $VARIABLE != "some value"` You can compare the values of two variables. For example: - `if: $VARIABLE_1 == $VARIABLE_2` - `if: $VARIABLE_1 != $VARIABLE_2` You can compare a variable to the `null` keyword to see if it is defined. For example: - `if: $VARIABLE == null` - `if: $VARIABLE != null` You can check if a variable is defined but empty. For example: - `if: $VARIABLE == ""` - `if: $VARIABLE != ""` You can check if a variable is both defined and not empty by using just the variable name in the expression. For example: - `if: $VARIABLE` You can also [use CI/CD inputs in variable expressions](https://docs.gitlab.com/ci/inputs/examples/#use-cicd-inputs-in-variable-expressions). ### Compare a variable to a regular expression You can do regular expression matching on variable values with the `=~` and `!~` operators. Expressions evaluate as `true` if: - Matches are found when using `=~`. - Matches are not found when using `!~`. For example: - `if: $VARIABLE =~ /^content.*/` - `if: $VARIABLE !~ /^content.*/` Additionally: - Single-character regular expressions, like `/./`, are not supported and produce an `invalid expression syntax` error. - Pattern matching is case-sensitive by default. Use the `i` flag modifier to make a pattern case-insensitive. For example: `/pattern/i`. - Only the tag or branch name can be matched by a regular expression. The repository path, if given, is always matched literally. - The entire pattern must be surrounded by `/`. For example, you can’t use `issue-/.*/` to match all tag names or branch names that begin with `issue-`, but you can use `/issue-.*/`. - The `@` symbol denotes the beginning of a ref’s repository path. To match a ref name that contains the `@` character in a regular expression, you must use the hex character code match `\x40`. - Use anchors `^` and `$` to avoid the regular expression matching only a substring of the tag name or branch name. For example, `/^issue-.*$/` is equivalent to `/^issue-/`, while just `/issue/` would also match a branch called `severe-issues`. - Variable pattern matching with regular expressions uses the [RE2 regular expression syntax](https://github.com/google/re2/wiki/Syntax). ### Store a regular expression in a variable Variables on the right side of `=~` and `!~` expressions are evaluated as regular expressions. The regular expression must be enclosed in forward slashes (`/`). For example: yaml ``` variables: pattern: '/^ab.*/' regex-job1: variables: teststring: 'abcde' script: echo "This job will run, because 'abcde' matches the /^ab.*/ pattern." rules: - if: '$teststring =~ $pattern' regex-job2: variables: teststring: 'fghij' script: echo "This job will not run, because 'fghi' does not match the /^ab.*/ pattern." rules: - if: '$teststring =~ $pattern' ``` Variables in a regular expression are not expanded. For example: yaml ``` variables: string1: 'regex-job1' string2: 'regex-job2' pattern: '/$string2/' regex-job1: script: echo "This job will NOT run, because the 'string1' variable inside the regex pattern is not expanded." rules: - if: '$CI_JOB_NAME =~ /$string1/' regex-job2: script: echo "This job will NOT run, because the 'string2' variable inside the 'pattern' variable is not expanded." rules: - if: '$CI_JOB_NAME =~ $pattern' ``` ### Join variable expressions together You can join multiple expressions using `&&` (and) or `||` (or), for example: - `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 == "something"` - `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 =~ /thing$/ && $VARIABLE3` - `$VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/ && $VARIABLE3` You can use parentheses to group expressions together. Parentheses take precedence over `&&` and `||`, so expressions enclosed in parentheses evaluate first, and the result is used for the rest of the expression. For the precedence of operators, `&&` evaluates before `||`. Nest parentheses to create complex conditions, and the inner-most expressions in parentheses evaluate first. For example: - `($VARIABLE1 =~ /^content.*/ || $VARIABLE2) && ($VARIABLE3 =~ /thing$/ || $VARIABLE4)` - `($VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/) && $VARIABLE3` - `$CI_COMMIT_BRANCH == "my-branch" || (($VARIABLE1 == "thing" || $VARIABLE2 == "thing") && $VARIABLE3)` ## Migrate from `only` or `except` to `rules` Use `rules` and CI/CD variable expressions to reproduce the same behavior as the deprecated [`only` and `except` keywords](https://docs.gitlab.com/ci/yaml/deprecated_keywords/#only--except). For example, starting with this deprecated configuration: yaml ``` job1: script: echo only: - main - /^stable-branch.*$/ - schedules job2: script: echo except: - main - /^issue-.*$/ - merge_requests ``` In this example: - `job1` uses `only` to run in pipelines when: - The branch is the default branch (`main`). - The branch name matches the pattern `/^stable-branch.*$/`. - The pipeline runs on a schedule. - `job2` uses `except` to skip pipelines when: - The branch is the default branch (`main`). - The branch name matches the pattern `/^issue-.*$/`. - The pipeline is a merge request pipeline. To create similar pipeline configuration with `rules`, use CI/CD variable expressions. For example, for a direct migration from `only` and `except` to `rules`: yaml ``` job1: script: echo rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH =~ /^stable-branch.*$/ - if: $CI_PIPELINE_SOURCE == "schedule" job2: script: echo rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: never - if: $CI_COMMIT_BRANCH =~ /^issue-.*$/ when: never - when: on_success ``` Both jobs behave the same way with `rules` as with `only` and `except`. However, you can simplify `job2` to avoid `when: never` rules. Define rules for when `job2` should run instead of when it should not run. For example, if `job2` should run for all branches except the default branch, and also for tags: yaml ``` job2: script: echo rules: - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH - if: $CI_COMMIT_TAG ``` In this example, `job2` runs when the branch is not the default branch, and when a new Git tag is created. Otherwise, the job does not run. ## Troubleshooting ### Unexpected behavior from regular expression matching with `=~` When using the `=~` character, make sure the right side of the comparison always contains a valid regular expression. If the right side of the comparison is not a valid regular expression enclosed with `/` characters, the expression evaluates in an unexpected way. In that case, the comparison checks if the left side is a substring of the right side. For example, `"23" =~ "1234"` evaluates to true, which is the opposite of `"23" =~ /1234/`, which evaluates to false. You should not configure your pipeline to rely on this behavior.
Shard184 (laksa)
Root Hash17323278190169633584
Unparsed URLcom,gitlab!docs,/ci/jobs/job_rules/ s443