英文:
How to combine multiple GitLab rules with And?
问题
根据文档,多个规则使用OR
操作进行组合,确实表现出这种方式。但我有一个特定的情况。
所以我有一组用于PCF
平台的作业,另一组用于Kubernetes
平台。它们通过设置变量简单地进行管理。可能需要同时激活两者:
.pcf-platform:
rules:
- if: $PLATFORM_PCF == "true"
.k8s-platform:
rules:
- if: $PLATFORM_K8S == "true"
.manual-feature-branch-optional:
rules:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule" && $CI_PIPELINE_SOURCE != "merge_request_event"
when: manual
allow_failure: true
.manual-default-branch-optional:
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule"
when: manual
allow_failure: true
现在我们想象有这些作业:
deploy-dev-pcf:
extends:
- .manual-feature-branch-optional
- .pcf-platform
script:
- do deploy
deploy-dev-k8s:
extends:
- .manual-feature-branch-optional
- .k8s-platform
script:
- do deploy
deploy-prod-pcf:
extends:
- .manual-default-branch-optional
- .pcf-platform
script:
- do deploy
现在,假设PLATFORM_K8S
和PLATFORM_PCF
都设置为true
,并且当前分支是功能分支(非默认);如预期,将deploy-dev-pcf
和deploy-dev-k8s
添加到流水线,以及deploy-prod-pcf
。
似乎第一个规则检查当前分支是否为默认分支
的解决方式是false
,但然后它评估下一个规则,检查PLATFORM_PCF是否等于true
,因为它为true,所以在我们仍然在功能分支时添加了该作业。
我需要以某种方式使这两个单独的规则成为And
,这样我就可以根据所需的平台和分支类型启用/禁用作业。
我可以为Kubernetes
创建一组规则,为PCF
创建一组规则,但这会不合理地增加规则数量,我对这种方法不情愿。
文档中说only
子句正在使用And
进行评估,但似乎only
正在被弃用,而rules
正在取而代之。
我该如何实现这一点?
英文:
According to the documentation, multiple rules are being combined with OR
operation, and indeed it behaves that way. But I have a specific situation.
So I have a set of jobs that are used for PCF
platform and another set for Kubernetes
platform. They are managed simply by setting variables. And it is possible that we may need both to be active:
.pcf-platform:
rules:
- if: $PLATFORM_PCF == "true"
.k8s-platform:
rules:
- if: $PLATFORM_K8S == "true"
.manual-feature-branch-optional:
rules:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule" && $CI_PIPELINE_SOURCE != "merge_request_event"
when: manual
allow_failure: true
.manual-default-branch-optional:
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule"
when: manual
allow_failure: true
Now we imagine we have these jobs:
deploy-dev-pcf:
extends:
- .manual-feature-branch-optional
- .pcf-platform
script:
- do deploy
deploy-dev-k8s:
extends:
- .manual-feature-branch-optional
- .k8s-platform
script:
- do deploy
deploy-prod-pcf:
extends:
- .manual-default-branch-optional
- .pcf-platform
script:
- do deploy
Now, imagine both PLATFORM_K8S
and PLATFORM_PCF
are set to true
and the current branch is a feature branch (not default); as expected, both deploy-dev-pcf
and deploy-dev-k8s
are added to the pipeline but so as deploy-prod-pcf
.
It seems the first rule that checks if the current branch is default branch
is resolved to false
but then it evaluates the next rule that checks PLATFORM_PCF equals to true
and since it is true it adds the job while we are still in a feature branch.
I need to somehow make these two separate rules being And
-ed so I can turn on/off jobs depending on the desired platform as well as the type of branch.
I could create one set of rules for Kubernetes
and one set for PCF
but that increases the number of rules unreasonably and I am reluctant to that approach.
In the documentation it is saying that only
clauses are being evaluated with And
but it seems only
is being deprecated in favour of rules
.
How can I achieve this?
答案1
得分: 2
为了正确理解这一点,您需要了解:
- 如何将作业合并在一起;
- 如何评估规则
或者您可以直接跳到最后一个标题来获取答案
如何将作业合并在一起
您在这里的问题的一部分似乎是您对extends:
在这种情况下的期望的问题。当数组值通过extends:
相遇时,它们会互相覆盖,而不是组合在一起。请参阅合并详情以获取额外的上下文。
例如,考虑到这个配置:
.foo:
rules:
- if: $FOO == "foo"
.bar:
rules:
- if: $BAR == "bar"
job:
extends:
- foo
- bar # 这里的规则数组将覆盖之前的声明
换句话说,在这种情况下,只有这些规则中的一个集合将在最终的配置中生效,就像这样写的一样:
job:
rules:
- if: $BAR == "bar"
所以,在您的示例中,只有平台规则被应用,这导致了您观察到的行为。
一个解决方法是使用!reference
来构建您的规则数组:
rules:
- !reference [.foo, rules]
- !reference [.bar, rules]
如何评估规则
这里需要理解的另一个方面是如何评估规则。GitLab将按顺序评估每个规则,并在第一个评估为true的规则处停止。最多只能有一个规则生效!如果没有规则匹配,该作业将被排除在管道之外。
因此,您可以以AND
评估的方式有两种方法。
您可以将它们合并为单个规则,并使用&&
运算符组合:
rules:
- if: $FOO == "foo" && $BAR == "bar"
您还可以通过将不同规则按特定顺序排序并使用与when: never
组合的逆逻辑来获得所需的效果。例如,这些规则具有与上述相同的效果:
rules:
- if: $FOO != "foo"
when: never
- if: $BAR != "bar"
when: never
- when: on_success # 基础情况很重要!
在这里反转逻辑(使用!=
而不是==
),我们允许评估继续到后续规则。如果您想要单独定义和重用规则,这一点非常重要。
在实践中
应用上述解释的理解,使用您的示例,您可以像这样做,这样您可以只定义每个规则一次:
# 定义不同的规则集
.rules:
pcf-only:
- if: $PCF_PLATFORM != "true"
when: never
k8s-only:
- if: $K8S_PLATFORM != "true"
when: never
manual-feature-branch-optional:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule" && $CI_PIPELINE_SOURCE != "merge_request_event"
when: manual
allow_failure: true
# 将这些规则以不同的方式组合在一起,以便重用
.k8s-deploy:
rules: # 顺序很重要!
- !reference [.rules, k8s-only]
- !reference [.rules, manual-feature-branch-optional]
.pcf-deploy:
rules:
- !reference [.rules, pcf-only]
- !reference [.rules, manual-feature-branch-optional]
# 根据需要使用`extends:`来扩展每个规则组合
deploy-dev-pcf:
extends:
- .pcf-deploy
script:
- 进行部署
# 或者,您可以直接定义规则,而不是使用`extends:`
deploy-dev-k8s:
rules:
- !reference [.rules, k8s-only]
- !reference [.rules, manual-feature-branch-optional]
script:
- 进行部署
# ...等等
在这里,您可能还有其他的重构机会,但这可能不是问题的关键点
英文:
In order to get this right, you'll need to understand:
- How jobs are merged together; and
- How rules are evaluated
Or you can just skip to the last heading for your answer
How jobs are merged together
Part of your problem here seems to be your expectation of what extends:
does in this case. When array values meet through extends:
, they override one another and do not combine. See merge details for additional context.
For example, given this configuration:
.foo:
rules:
- if: $FOO == "foo"
.bar:
rules:
- if: $BAR == "bar"
job:
extends:
- foo
- bar # array of rules here will override the previous declarations
In other words, only one set of those rules will be effective in the resulting configuration in this case, as if written like so:
job:
rules:
- if: $BAR == "bar"
So, in your example, only the platform rule is being applied, which results in the behavior you are observing.
One way to get around this is to use !reference
instead to build your rules array:
rules:
- !reference [.foo, rules]
- !reference [.bar, rules]
How rules are evaluated
Another aspect that needs understanding here is how rules are evaluated. GitLab will evaluate each rule in order and stop at the first rule that evaluates true. There can be at most, one rule that takes effect! If no rules match, the job is excluded from the pipeline.
So, there are a couple ways you can get the effect of AND
evaluation.
You can combine it into a single rule and combine with &&
operators:
rules:
- if: $FOO == "foo" && $BAR == "bar"
You can also get the effect you want by ordering different rules in a particular order and using the inverse logic combined with when: never
. For example, these rules have the same effect as above:
rules:
- if: $FOO != "foo"
when: never
- if: $BAR != "bar"
when: never
- when: on_success # base case is important!
Inverting the logic here (checking !=
instead of ==
), we allow evaluation to continue to subsequent rules. This is important if you want to define and re-use rules separately.
In practice
Applying the understandings explained above, using your example, you might do something like this which allows you to define each rule just once:
# define different sets of rules
.rules:
pcf-only:
- if: $PCF_PLATFORM != "true"
when: never
k8s-only:
- if: $K8S_PLATFORM != "true"
when: never
manual-feature-branch-optional:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule" && $CI_PIPELINE_SOURCE != "merge_request_event"
when: manual
allow_failure: true
# Combine these rules in different ways that can be reused
.k8s-deploy:
rules: # Order matters!
- !reference [.rules, k8s-only]
- !reference [.rules, manual-feature-branch-optional]
.pcf-deploy:
rules:
- !reference [.rules, pcf-only]
- !reference [.rules, manual-feature-branch-optional]
# Use `extends:` for each combination of rules as-needed
deploy-dev-pcf:
extends:
- .pcf-deploy
script:
- do deploy
# alternatively, you can define rules directly instead of using `extends:`
deploy-dev-k8s:
rules:
- !reference [.rules, k8s-only]
- !reference [.rules, manual-feature-branch-optional]
script:
- do deploy
# ... and so on
There's probably other refactoring opportunities for you here, but that's perhaps beside the point of the question
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论