如何让 Gitlab CI 管道始终运行一些作业,而其他作业仅在合并请求上运行?

Posted

技术标签:

【中文标题】如何让 Gitlab CI 管道始终运行一些作业,而其他作业仅在合并请求上运行?【英文标题】:How to have a Gitlab CI Pipeline run some jobs always and other jobs only on merge-requests? 【发布时间】:2020-05-05 04:11:04 【问题描述】:

TL/DR: 我的目标是拥有一个 Gitlab (CE-12.4.2) 管道,它只在合并请求和其他作业上执行一些作业(在合并请求和所有正常推送上)。 .gitlab-ci.yml 必须如何做到这一点?

我的用例: 我有一个运行很多工作的大管道(测试、验证、dep、构建、文档......)。现在我添加了一个暂存环境(使用 kubernetes)并让管道构建一个新映像并将其部署在暂存环境中。这使我可以立即打开更改后的(网络)应用程序并查看更改的行为和外观,而无需在本地检查它们。现在构建一个映像并将其部署到 staging 对每次推送来说都需要大量资源,因此我只希望在有人创建合并请求供我审查时进行部署。

一个非常简单的例子:

install:
  script: ...

test:
  script: ...

build-image:
  script: ...
  only: [merge_requests]

deploy-staging:
  script: ...
  only: [merge_requests]

对于所有正常推送,应执行作业installtest

对于合并请求,应执行作业 installtestbuild-imagedeploy-staging

我尝试过的: Gitlab 具有在作业上定义only: [merge_requests] 的此功能,这会导致该作业仅在为合并请求执行管道时才执行。听起来正是我正在寻找的东西,但有一个很大的收获。一旦该属性应用于管道中的一个作业,该管道中没有该属性的所有其他作业将在合并请求中执行时从管道中删除。起初对我来说这似乎是一个错误,但实际上是documented behaviour:

In the above example, the pipeline contains only a test job. Since the build and deploy jobs don’t have the only: [merge_requests] parameter, they will not run in the merge request.

为了将所有其他作业重新引入管道以进行合并请求,我必须将only: [merge_requests] 应用于所有其他作业。这种方法的问题在于,现在这些常规作业不再为正常的 git-push 执行。而且我无法将这些常规作业重新引入管道以进行正常推送,因为 Gitlab 不支持 only: [always] 或类似的东西。

现在我还注意到 only 语法是弃用的候选者,人们应该更喜欢 rules 语法,所以我看了一下。这种方法存在多个问题:

使用rules 检测管道是否针对合并请求执行的唯一方法是评估与合并请求相关的变量,例如$CI_MERGE_REQUEST_ID。不幸的是,这些变量仅在使用only: [merge_requests] 时才存在,这将重新引入上述问题。 规则只允许有条件地应用其他属性,所以我仍然必须使用onlyexceptwhen 属性来实际从管道中删除或添加作业。不幸的是,Gitlab 不支持 only: [never]when: never 之类的东西,所以我无法实际删除或添加这些作业。

我还尝试使用 needdependencies 属性让作业依赖于另一个作业,这似乎对作业是否包含在管道中没有影响。

我拼命尝试的最后一件事是始终包含所有作业,只需将它们标记为when: manual,即可通过按下按钮手动触发。这有点工作,但非常乏味,因为部署到登台是一个多作业过程,每项作业都需要相当长的时间才能完成。所以我会看到一个合并请求,按下第一个作业的按钮,等待 5 分钟,按下下一个按钮,再次等待 5 分钟,然后才能使用暂存。对于许多小的合并请求,这会占用我很多时间并且不是一个有效的解决方案。我也不能只将这些作业中的第一个标记为手动,因为 Gitlab 将跳过该作业并无序执行后面的作业(同样,needsdependencies 在手动处理时似乎对此没有影响触发的作业)。

让我有点困惑的是,在网上搜索后,我发现没有人有同样的问题。要么我是唯一一个只想为合并请求执行某些作业而不排除所有其他作业(这似乎极不可能)的 Gitlab 用户,要么我错过了一些明显的东西(这似乎更有可能)。我错过了什么还是 Gitlab 真的不支持这个用例?

【问题讨论】:

把这个扔出去,你能用:only: - merge_requests - pushes 用于installtest 的正常推送阶段吗? 我感受到你的痛苦,你并不孤单:/ 【参考方案1】:

可以将一些作业添加到 MR 管道。我从这里得到了例子: https://docs.gitlab.com/ee/ci/merge_request_pipelines/index.html#excluding-certain-jobs

总体思路:为每个作业定义only: [branch, merge_requests],并为部署作业重写only: [merge_requests]

.only-default: &only-default
  only:
    - branches
    - merge_requests

build:
  stage: build
  tags:
    - build
  <<: *only-default
  script:
    - echo build

deploy mr:
  stage: deploy
  tags:
    - build
  only:
    - merge_requests
  script:
    - echo "deploy on MR"

但是有一个问题。此示例在创建 MR 后为每次推送启动两个管道。一种用于only: [branch],一种用于only: [merge_requests]。这就是only: [merge_requests] 的工作方式,它会在每次 MR 更改时启动工作。新的提交绝对是一个变化。

似乎没有解决方案可以仅针对某些事件启动管道 - 例如 MR 创建。

但我认为一些解决方法是可能的:

    每个管道都存在部署暂存作业 作业脚本检查是否为当前分支创建了 MR 如果创建了 MR,则为该 MR 设置脚本标签,例如“部署部署” 如果此标签已经存在,或者没有创建 MR,则什么也不做。

GitLab API、curl 和 jq 可以帮助解决这个问题。如果我有空闲时间,我一定会尝试写它:)。

【讨论】:

【参考方案2】:

来自previous answer:

但是有一个问题。此示例在创建 MR 后为每次推送启动两个管道。一张给only: [branch],一张给only: [merge_requests]

检查最后一个GitLab 13.8(2021 年 1 月)是否有帮助:

同时使用分支和 MR 管道,不要重复

以前无法先为分支运行管道,然后在创建 MR 时切换到merge requests pipelines。

因此,对于某些配置,如果分支上已打开合并请求,则推送到分支可能会导致重复的管道:分支上的一个管道和合并请求的另一个管道。

现在您可以在 CI 配置中使用新的 $CI_OPEN_MERGE_REQUESTS 预定义环境变量,以便在正确的时间从分支管道切换到 MR 管道,并防止冗余管道。

请参阅 Documentation 和 Issue。

user2993689 在the comments 中指出了类似示例的文档:

Avoid duplicate pipeline

【讨论】:

Gitlab 文档中给出了类似的示例:docs.gitlab.com/ee/ci/jobs/…。 @user2993689 谢谢。我已将您的评论添加到答案中以提高知名度。 我在这个解决方案中发现的问题是,它会导致由推送到 MR 触发的管道分离管道,我认为如果没有推送到 MR,这不是你所期望的workflowrules 在您的配置中。但是,我可能错了。 @user2993689 很有趣。当你想通了,不要犹豫直接编辑这个答案

以上是关于如何让 Gitlab CI 管道始终运行一些作业,而其他作业仅在合并请求上运行?的主要内容,如果未能解决你的问题,请参考以下文章

如何使手动作业始终在 GitLab CI 上成功退出?

如何在 GitLab CI 的管道中将变量的值从一个作业传递到下一个作业?

GITLAB CI 管道,仅使用 git 标签运行作业

使用 Testcontainers 执行运行集成测试的 Gitlab CI 管道作业时出现问题

GitLab CI - 添加标签时避免构建

使用 Gitlab CI 管道时,作业名称中的冒号是啥意思?