Help us learn about your current experience with the documentation. Take the survey.

使用 rules 指定作业何时运行

  • Tier: 免费版、高级版、旗舰版
  • Offering: GitLab.com、GitLab 自托管、GitLab 专用

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

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

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

我们正在 改进 rules 的史诗 中讨论未来的关键字改进,任何人都可以在此处添加建议或请求。

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

如果您使用 when 子句作为最终规则(不包括 when: never),可能会启动两个同时运行的流水线。推送流水线和合并请求流水线都可能由同一事件触发(对打开的合并请求的源分支进行推送)。有关更多详细信息,请参阅如何防止重复流水线

为定时流水线运行作业

您可以配置作业仅在流水线被定时执行时运行。例如:

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 当您使用 GitLab 以外的 CI 服务时。
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 push 事件触发的流水线,包括分支和标签。
schedule 定时流水线
security_orchestration_policy 定时扫描执行策略 流水线。
trigger 使用 触发令牌 创建的流水线。
web 在 GitLab UI 中从项目的 构建 > 流水线 部分选择 新建流水线 创建的流水线。
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,单个操作(如向分支推送提交)可能会触发多个流水线。您不必为意外触发的多种流水线类型显式配置规则。

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

例如:

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"

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

例如,以下不会触发双流水线,但没有 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

您还可以 在变量表达式中使用 CI/CD 输入

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

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

如果以下情况,表达式评估为 true

  • 使用 =~ 时找到匹配项。
  • 使用 !~ 时未找到匹配项。

例如:

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

此外:

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

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

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

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。

您不应配置流水线来依赖此行为。