{{< details >}}

  • Tier: 基础版, 专业版, 旗舰版
  • Offering: JihuLab.com, 私有化部署

{{< /details >}}

使用 rules 在流水线中包含或排除作业。

规则按顺序进行评估,直到找到第一个匹配项。找到匹配项时,作业会根据配置被包含或排除在流水线之外。

你不能在规则中使用在作业脚本中创建的 dotenv 变量,因为规则在任何作业运行之前就被评估。

rules 示例

以下示例使用 if 定义作业仅在两个特定情况下运行:

job:
  script: echo "Hello, Rules!"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      when: manual
      allow_failure: true
    - if: $CI_PIPELINE_SOURCE == "schedule"
  • 如果流水线是为了合并请求,则第一个规则匹配,作业被添加到合并请求流水线,具有以下属性:
    • when: manual(手动作业)
    • allow_failure: true(即使手动作业未运行,流水线也会继续运行)
  • 如果流水线不是为了合并请求,则第一个规则不匹配,第二个规则被评估。
  • 如果流水线是一个计划的流水线,则第二个规则匹配,作业被添加到计划的流水线中。由于未定义任何属性,因此它将以以下方式添加:
    • when: on_success(默认)
    • allow_failure: false(默认)
  • 所有其他情况下,没有规则匹配,因此作业不会添加到任何其他流水线中。

或者,你可以定义一组规则在少数情况下排除作业,而在所有其他情况下运行它们:

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
  • 如果流水线是为了合并请求,作业不会被添加到流水线中。
  • 如果流水线是一个计划的流水线,作业不会被添加到流水线中。
  • 所有其他情况下,作业会被添加到流水线中,使用 when: on_success

{{< alert type=”warning” >}}

如果你使用 when 子句作为最终规则(不包括 when: never),可能会启动两个同时进行的流水线。推送流水线和合并请求流水线可能会因同一事件触发(推送到一个开放的合并请求的源分支)。请参阅如何防止重复的流水线了解更多详情。

{{< /alert >}}

为计划的流水线运行作业

你可以配置一个作业仅在流水线被计划时执行。例如:

job:on-schedule:
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
  script:
    - make world

job:
  rules:
    - if: $CI_PIPELINE_SOURCE == "push"
  script:
    - make build

在此示例中,make world 在计划的流水线中运行,而 make build 在分支和标签流水线中运行。

如果分支为空则跳过作业

使用 rules:changes:compare_to 跳过在分支为空时的作业,从而节省 CI/CD 资源。配置将分支与默认分支进行比较,如果分支:

  • 没有更改的文件,作业不会运行。
  • 有更改的文件,作业会运行。

例如,在一个以 main 为默认分支的项目中:

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:
          - '**/*'

此作业的规则将当前分支中的所有文件和路径递归地(**/*)与 main 分支进行比较。规则匹配且只有当分支中的文件有更改时,作业才会运行。

常见 if 子句与预定义变量

rules:if 子句通常与预定义的 CI/CD 变量一起使用,特别是 CI_PIPELINE_SOURCE 预定义变量

以下示例将作业作为手动作业在计划的流水线或推送流水线(到分支或标签)中运行,使用 when: on_success(默认)。它不会将作业添加到任何其他流水线类型中。

job:
  script: echo "Hello, Rules!"
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
      when: manual
      allow_failure: true
    - if: $CI_PIPELINE_SOURCE == "push"

以下示例在合并请求流水线和计划的流水线中将作业作为 when: on_success 作业运行。它不会在任何其他流水线类型中运行。

job:
  script: echo "Hello, Rules!"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_PIPELINE_SOURCE == "schedule"

其他常用的 if 子句:

  • if: $CI_COMMIT_TAG: 如果更改是为标签推送的。
  • if: $CI_COMMIT_BRANCH: 如果更改是推送到任何分支的。
  • if: $CI_COMMIT_BRANCH == "main": 如果更改是推送到 main 的。
  • if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH: 如果更改是推送到默认分支的。当你想在多个项目中具有相同配置但默认分支不同的情况下使用。
  • if: $CI_COMMIT_BRANCH =~ /regex-expression/: 如果提交分支匹配正则表达式。
  • if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_TITLE =~ /Merge branch.*/: 如果提交分支是默认分支且提交消息标题匹配正则表达式。例如,合并提交的默认提交消息以 Merge branch 开头。
  • if: $CUSTOM_VARIABLE == "value1": 如果自定义变量 CUSTOM_VARIABLE 恰好是 value1

仅在特定的流水线类型中运行作业

你可以使用预定义的 CI/CD 变量rules 来选择作业应运行的流水线类型。

下表列出了一些可以使用的变量,以及这些变量可以控制的流水线类型:

  • 分支流水线:运行在 Git push 事件到分支时,如新提交或标签。
  • 标签流水线:仅在新的 Git 标签推送到分支时运行。
  • 合并请求流水线:运行在合并请求的更改时,如新的提交或在合并请求的流水线选项卡中选择运行流水线
  • 计划的流水线
变量 分支 标签 合并请求 计划
CI_COMMIT_BRANCH    
CI_COMMIT_TAG     是,如果计划的流水线被配置为在标签上运行。
CI_PIPELINE_SOURCE = push    
CI_PIPELINE_SOURCE = schedule      
CI_PIPELINE_SOURCE = merge_request_event      
CI_MERGE_REQUEST_IID      

例如,要配置一个作业以在合并请求流水线和计划的流水线中运行,而不是分支或标签流水线:

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 预定义变量

使用 CI_PIPELINE_SOURCE 变量控制何时为这些流水线类型添加作业:

描述
api 为通过 流水线 API 触发的流水线。
chat 为使用 极狐GitLab ChatOps 命令创建的流水线。
external 当你使用其他 CI 服务而不是极狐GitLab 时。
external_pull_request_event GitHub 上的外部拉取请求 被创建或更新时。
merge_request_event 为创建或更新合并请求时创建的流水线。需要启用合并请求流水线合并结果流水线合并列车
ondemand_dast_scan DAST 按需扫描 流水线。
ondemand_dast_validation DAST 按需验证 流水线。
parent_pipeline 为由 父/子流水线 触发的流水线。使用此流水线源在子流水线配置中,以便它可以由父流水线触发。
pipeline 多项目流水线,由使用 API 和 CI_JOB_TOKENtrigger 关键字创建。
push 为由 Git 推送事件触发的流水线,包括分支和标签。
schedule 计划的流水线
security_orchestration_policy 安全编排策略 流水线。
trigger 为使用触发令牌 创建的流水线。
web 为通过极狐GitLab UI 中的项目 Build > Pipelines 部分选择新建流水线创建的流水线。
webide 为使用 Web IDE 创建的流水线。

这些值与使用 流水线 API 端点 时返回的 source 参数值相同。

复杂规则

你可以在同一规则中使用所有 rules 关键字,例如 ifchangesexists。只有当所有包含的关键字评估为 true 时,规则才会评估为 true。

例如:

docker build:
  script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
  rules:
    - if: $VAR == "string value"
      changes:  # 如果以下任何路径匹配修改过的文件,则包含作业并设置为 when:manual。
        - Dockerfile
        - docker/scripts/**/*
      when: manual
      allow_failure: true

如果 Dockerfile 文件或 /docker/scripts 中的任何文件发生更改并且 $VAR == “string value”,则作业手动运行并允许失败。

你可以使用括号&&|| 组合来构建更复杂的变量表达式。

job1:
  script:
    - echo This rule uses parentheses.
  rules:
    - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE

避免重复的流水线

如果一个作业使用 rules,一个动作(如向分支推送提交)可以触发多个流水线。你不需要显式地为多个类型的流水线配置规则来意外触发它们。

有可能导致重复流水线的一些配置会显示pipeline 警告

例如:

job:
  script: echo "This job creates double pipelines!"
  rules:
    - if: $CUSTOM_VARIABLE == "false"
      when: never
    - when: always

此作业在 $CUSTOM_VARIABLE 为 false 时不会运行,但它在所有其他流水线中运行,包括推送(分支)和合并请求流水线。使用此配置,每次推送到一个开放合并请求的源分支都会导致重复的流水线。

为了避免重复的流水线,你可以:

  • 使用 workflow 指定可以运行哪些类型的流水线。
  • 重写规则以仅在非常特定的情况下运行作业,并避免使用最终的 when 规则:

    job:
      script: echo "This job does NOT create double pipelines!"
      rules:
        - if: $CUSTOM_VARIABLE == "true" && $CI_PIPELINE_SOURCE == "merge_request_event"
    

你也可以通过更改作业规则来避免推送(分支)流水线或合并请求流水线来避免重复的流水线。但是,如果你使用 - when: always 规则而没有使用 workflow: rules,极狐GitLab 仍然会显示pipeline 警告

例如,以下不会触发重复的流水线,但不推荐在没有 workflow: rules 的情况下使用:

job:
  script: echo "This job does NOT create double pipelines!"
  rules:
    - if: $CI_PIPELINE_SOURCE == "push"
      when: never
    - when: always

你不应该在同一作业中同时包含推送和合并请求流水线而没有防止重复流水线的 workflow:rules

job:
  script: echo "This job creates double pipelines!"
  rules:
    - if: $CI_PIPELINE_SOURCE == "push"
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

同时,不要在同一流水线中混合使用 only/except 作业和 rules 作业。它可能不会导致 YAML 错误,但 only/exceptrules 的不同默认行为可能会导致难以排查的问题:

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"

对于每次推送到分支的更改,都会运行重复的流水线。一个分支流水线运行一个作业(job-with-no-rules),一个合并请求流水线运行另一个作业(job-with-rules)。没有规则的作业默认是except: merge_requests,因此 job-with-no-rules 在所有情况下都会运行,除了合并请求。

在不同作业中重用规则

使用 !reference 标签 在不同作业中重用规则。你可以将 !reference 规则与常规作业定义的规则结合使用。例如:

.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 变量表达式

使用 rules:if 的变量表达式来控制何时应将作业添加到流水线。

你可以使用相等运算符 ==!= 将变量与字符串进行比较。单引号和双引号都有效。变量必须在比较的左侧。例如:

  • if: $VARIABLE == "some value"
  • if: $VARIABLE != "some value"

你可以比较两个变量的值。例如:

  • if: $VARIABLE_1 == $VARIABLE_2
  • if: $VARIABLE_1 != $VARIABLE_2

你可以将变量与 null 关键字进行比较以查看其是否已定义。例如:

  • if: $VARIABLE == null
  • if: $VARIABLE != null

你可以检查变量是否已定义但为空。例如:

  • if: $VARIABLE == ""
  • if: $VARIABLE != ""

你可以仅通过在表达式中使用变量名来检查变量是否已定义且不为空。例如:

  • if: $VARIABLE

将变量与正则表达式进行比较

你可以使用 =~!~ 运算符对变量值进行正则表达式匹配。使用正则表达式进行变量模式匹配采用 RE2 正则表达式语法。

表达式评估为 true 如果:

  • 使用 =~ 时找到了匹配项。
  • 使用 !~没有找到匹配项。

例如:

  • if: $VARIABLE =~ /^content.*/
  • if: $VARIABLE !~ /^content.*/

另外:

  • 单字符正则表达式,如 /./,不支持并会产生 invalid expression syntax 错误。
  • 模式匹配默认是区分大小写的。使用 i 标志修饰符使模式不区分大小写。例如:/pattern/i
  • 只有标签或分支名称可以通过正则表达式匹配。如果给定,存储库路径始终按字面匹配。
  • 整个模式必须用 / 包围。例如,你不能使用 issue-/.*/ 来匹配所有以 issue- 开头的标签名或分支名,但可以使用 /issue-.*/
  • @ 符号表示引用的存储库路径的开头。要在正则表达式中匹配包含 @ 字符的引用名称,必须使用十六进制字符代码匹配 \x40
  • 使用锚点 ^$ 避免正则表达式仅匹配标签名或分支名的子字符串。例如,/^issue-.*$/ 等效于 /^issue-/,而仅 /issue/ 还会匹配名为 severe-issues 的分支。

将正则表达式存储在变量中

{{< history >}}

  • 在极狐GitLab 15.0 引入,使用名为 ci_fix_rules_if_comparison_with_regexp_variable功能标志,默认禁用。
  • 在极狐GitLab 15.1 GA,功能标志 ci_fix_rules_if_comparison_with_regexp_variable 被移除。

{{< /history >}}

=~!~ 表达式的右侧,变量被评估为正则表达式。正则表达式必须用正斜杠(/)括起来。例如:

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:
  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 resolved."
  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 resolved."
  rules:
    - if: '$CI_JOB_NAME =~ $pattern'

将变量表达式组合在一起

你可以使用 &&(和)或 ||(或)组合多个表达式,例如:

  • $VARIABLE1 =~ /^content.*/ && $VARIABLE2 == "something"
  • $VARIABLE1 =~ /^content.*/ && $VARIABLE2 =~ /thing$/ && $VARIABLE3
  • $VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/ && $VARIABLE3

运算符的优先级遵循 Ruby 2.5 标准,所以 &&|| 之前评估。

你可以使用括号将表达式组合在一起。括号优先于 &&||,因此括号中的表达式首先评估,结果用于表达式的其余部分。

嵌套括号以创建复杂的条件,并且括号中的最内层表达式首先评估。例如:

  • ($VARIABLE1 =~ /^content.*/ || $VARIABLE2) && ($VARIABLE3 =~ /thing$/ || $VARIABLE4)
  • ($VARIABLE1 =~ /^content.*/ || $VARIABLE2 =~ /thing$/) && $VARIABLE3
  • $CI_COMMIT_BRANCH == "my-branch" || (($VARIABLE1 == "thing" || $VARIABLE2 == "thing") && $VARIABLE3)

故障排除

使用 =~ 进行正则表达式匹配时的意外行为

当使用 =~ 字符时,请确保比较的右侧始终包含一个有效的正则表达式,并用 / 字符括起来。

如果比较的右侧不是一个用 / 字符括起来的有效正则表达式,表达式会以意想不到的方式进行评估。在这种情况下,比较会检查左侧是否是右侧的子字符串。例如,"23" =~ "1234" 评估为 true,这与 "23" =~ /1234/ 相反,后者评估为 false。

你不应该将你的流水线配置为依赖于这种行为。