极狐GitLab 项目的流水线
为 gitlab-org/gitlab 配置的流水线(以及 dev 实例的)是在常规的 .gitlab-ci.yml 中配置的,该文件本身包含了 .gitlab/ci/ 下的文件以便于维护。
我们尽可能地在 内部试用 极狐GitLab CI/CD 功能和最佳实践。
不要在 gitlab-org/gitlab 流水线中使用 CI/CD 组件,除非它们在 dev.gitlab.com 实例上有镜像。CI/CD 组件不能跨不同实例工作,并且如果 dev.gitlab.com 镜像上不存在它们,会导致流水线失败。
流水线层级
一个合并请求通常会运行多个 CI/CD 流水线。根据合并请求在审批流程中的位置,我们会触发不同类型的流水线。我们称这些类型的流水线为 流水线层级。
我们目前有三个层级:
- pipeline::tier-1:合并请求没有任何审批
- pipeline::tier-2:合并请求至少有一个审批,但仍需要更多审批
- pipeline::tier-3:合并请求已获得所有需要的审批
通常,流水线层级越低,流水线应该越快。层级越高,通过运行更多测试,流水线应该给我们带来越多的信心。
有关实施的更多信息,请参阅 在 MR 流水线中引入 "层级" 史诗。
合并请求获批前的预测性测试任务
为了降低流水线成本并缩短作业耗时,在合并请求获得批准之前,流水线将运行一组预测性的 RSpec 和 Jest 测试,这些测试很可能因合并请求的更改而失败。
合并请求获批后,流水线将包含完整的 RSpec 和 Jest 测试。这将确保在合并请求合并之前所有测试都已运行。
极狐GitLab 项目测试依赖概述
为了理解预测性测试任务是如何执行的,我们需要了解极狐GitLab 代码(前端和后端)与对应测试(Jest 和 RSpec)之间的依赖关系。这种依赖关系可以通过以下图表来可视化:
Rendering chart...
综上所述:
- RSpec 测试依赖后端代码。
- Jest 测试依赖前端和后端代码,其中后端通过前端 fixtures 依赖。
detect-tests CI 任务
大多数针对 gitlab-org/gitlab 的 CI/CD 流水线都会在 prepare 阶段运行一个 detect-tests CI 任务,以检测根据给定 MR 中更改的文件应该运行哪些后端/前端测试。
detect-tests 任务会创建许多文件,其中包含应该运行的后端/前端测试。这些文件将在流水线的后续任务中被读取,只有那些测试才会被执行。
RSpec 预测性任务
确定合并请求中的预测性 RSpec 测试文件
为了识别合并请求中可能失败的 RSpec 测试,我们使用动态映射和静态映射。
动态映射
首先,我们使用 test_file_finder gem,其动态映射策略来自 Crystalball gem(查看使用位置,以及我们在 Crystalball 中使用的映射策略)。
除了 test_file_finder,我们还添加了几个高级映射以检测更多需要运行的测试:
- FindChanges (!74003)
- 自动检测因后端变更而需运行的 Jest 测试(通过前端 fixtures)
- PartialToViewsMappings (#395016)
- 当 MR 中更改了包含在视图中的 Rails 局部模板时,运行视图规格测试
- JsToSystemSpecsMappings (#386754)
- 如果 MR 中更改了 JavaScript 文件,则运行某些系统规格测试
- GraphqlBaseTypeMappings (#386756)
- 如果 GraphQL 类型类发生更改,我们应该尝试找出可能包含此类型的其他 GraphQL 类型,并运行它们的规格测试。
- ViewToSystemSpecsMappings (#395017)
- 当视图发生更改时,我们尝试查找会测试该代码区域的功能规格测试。
- ViewToJsMappings (#386719)
- 如果 JS 文件发生更改,我们应该尝试找出涵盖此 JS 组件的系统规格测试。
- FindFilesUsingFeatureFlags (#407366)
- 如果功能标志发生更改,我们会检查哪些 Ruby 文件包含了该功能标志,并将其添加到 detect-tests CI 任务的已更改文件列表中。然后,该任务的其余部分将根据这些已更改文件检测应运行哪些前端/后端测试。
静态映射
我们使用 test_file_finder gem,并结合在 tests.yml 文件中维护的静态映射,来处理那些无法通过动态映射进行映射的特殊情况(查看使用位置)。
测试映射包含每个源文件到依赖于该源文件的测试文件列表的映射。
例外情况
此外,在少数情况下,我们始终会运行完整的 RSpec 测试:
- 当合并请求上设置了 pipeline:run-all-rspec 标签时。此标签将触发所有 RSpec 测试,包括在 as-if-foss 任务中运行的测试。
- 当合并请求上设置了 pipeline:mr-approved 标签,并且代码更改满足 backend-patterns 规则时。请注意,该标签由分类自动化在合并请求获得任何审查者批准时分配。不建议手动应用此标签。
- 当合并请求由自动化创建时(例如,Gitaly 更新或针对稳定分支的 MR)
- 当合并请求在安全镜像中创建时
- 当任何 CI 配置文件发生更改时(例如,.gitlab-ci.yml 或 .gitlab/ci/**/*)
是否遇到了后端预测性测试的问题?
如果是这样,请查看 预测性测试的开发分析 RUNBOOK 以获取有关如何处理预测性测试问题的说明。此外,如果你发现了任何测试选择缺口,请告知 @gl-dx/development-analytics,以便我们采取必要措施优化测试选择。
极狐GitLab Duo 系统测试选择
在 tier-2 流水线中,极狐GitLab Duo 会预测哪些系统规格测试与 MR 更改相关。该步骤与 detect-tests 任务一同运行,并专门针对系统规格测试(spec/features/ 和 ee/spec/features/)。
工作原理
detect-system-tests-duo-experiment 任务针对 MR 差异调用极狐GitLab Duo CLI。然后,PreparePredictiveSystemPipeline 脚本会处理输出并准备流水线输入:
- 极狐GitLab Duo 充满信心:极狐GitLab Duo 的预测与 detect-tests 选择的系统测试合并(并集)。预测性流水线运行合并后的集合。如果极狐GitLab Duo 预测不需要任何系统测试,则仅运行 detect-tests 选择的测试。
- 极狐GitLab Duo 信心不足或失败:从预测性流水线中剔除系统测试。完整的系统测试套件在一个专用的子流水线(rspec-predictive-system-full)中运行。
范围
极狐GitLab Duo 系统测试选择仅在 gitlab-org/gitlab 的 tier-2 流水线上运行。在以下情况下会跳过:
- 设置了 pipeline:run-all-rspec 标签(已运行完整套件)。
- 设置了 pipeline:spec-only 标签(detect-tests 已经直接识别出规格文件)。
- 更改符合核心后端或 workhorse 模式(对于这些更改,所有系统测试已在主流水线中运行)。
在 tier-3 流水线上,该任务会运行以收集指标,但其输出不会影响测试选择。
要在不更改代码的情况下禁用极狐GitLab Duo 系统测试选择,请将 CI/CD 项目变量 GLCI_DUO_SYSTEM_TESTS_DISABLED 设置为 "true"。禁用后,detect-system-tests-duo-experiment 任务会被跳过,完整的系统测试套件将作为 tier-2 的备选方案运行。
Jest 预测性任务
确定合并请求中的预测性 Jest 测试文件
为了识别合并请求中可能失败的 Jest 测试,我们使用 --findRelatedTests 选项将所有已更改文件的列表传递给 jest。在这种模式下,jest 会解析与已更改文件相关的所有依赖项,其中包括依赖链中包含这些文件的测试文件。
例外情况
此外,在少数情况下,我们始终会运行完整的 Jest 测试:
- 当合并请求上设置了 pipeline:run-all-jest 标签时
- 当合并请求由自动化创建时(例如,Gitaly 更新或针对稳定分支的 MR)
- 当合并请求在安全镜像中创建时
- 当相关的 CI 配置文件发生更改时(.gitlab/ci/rules.gitlab-ci.yml、.gitlab/ci/frontend.gitlab-ci.yml)
- 当任何前端依赖文件发生更改时(例如 package.json、yarn.lock、config/webpack.config.js、config/helpers/**/*.js)
- 当任何第三方 JavaScript 文件发生更改时(例如 vendor/assets/javascripts/**/*)
完整 Jest 测试的 rules 定义在 rules.gitlab-ci.yml 中的 .frontend:rules:jest 定义。
是否遇到了前端预测性测试的问题?
如果是这样,请查看 预测性测试的开发分析 RUNBOOK 以获取有关如何处理预测性测试问题的说明。
Fork 流水线
对于 fork 流水线,我们仅运行预测性的 RSpec 和 Jest 任务,除非在 MR 上设置了 pipeline:run-all-rspec 标签。其目标是减少 fork 流水线消耗的计算配额。
请参阅 实验性议题。
合并请求流水线中的快速失败任务
为了在合并请求破坏现有测试时提供更快的反馈,我们实施了快速失败机制。
在合并请求流水线中,会与所有其他 rspec 任务并行添加一个 rspec fail-fast 任务。该任务运行与合并请求中的更改直接相关的测试。
如果这些测试中有任何失败,rspec fail-fast 任务将失败,从而触发 fail-pipeline-early 任务运行。fail-pipeline-early 任务:
- 取消当前正在运行的流水线以及所有进行中的任务。
- 将流水线状态设置为 failed。
例如:
Rendering chart...
如果与合并请求相关的测试文件超过 10 个,rspec fail-fast 将不执行任何操作。这可以防止 rspec fail-fast 的持续时间超过 rspec 任务的平均持续时间,从而违背其目的。
可以通过设置名为 RSPEC_FAIL_FAST_TEST_FILE_COUNT_THRESHOLD 的 CI/CD 变量来覆盖此数字。
在合并请求流水线中重新运行之前失败的测试
为了缩短解决合并请求失败测试后的反馈时间,rspec rspec-pg17-rerun-previous-failed-tests 和 rspec rspec-ee-pg17-rerun-previous-failed-tests 任务会运行来自前一个 MR 流水线的失败测试。
此功能于 2021 年 8 月 25 日引入,随 https://jihulab.com/gitlab-cn/gitlab/-/merge_requests/69053 一起。
失败测试如何重新运行
- detect-previous-failed-tests 任务(prepare 阶段)检测与前一个 MR 流水线中失败的 RSpec 任务相关联的测试文件。
- rspec rspec-pg17-rerun-previous-failed-tests 和 rspec rspec-ee-pg17-rerun-previous-failed-tests 任务将运行 detect-previous-failed-tests 任务收集到的测试文件。
Rendering chart...
合并列车
当前用法
目前,合并列车流水线不运行任何测试:它们仅强制执行在启用合并列车之前就已存在但无法轻松强制执行的“合并合并请求”指南。
合并列车流水线运行一个单独的 pre-merge-checks 任务,以确保合并前的最新流水线符合以下条件:
- 是一个合并结果流水线
- 是一个 tier-3 流水线(完整流水线,而非预测性流水线)
- 创建时间不超过 16 小时(对于稳定分支为 72 小时)
我们开启了一个反馈议题以迭代此解决方案。
下一步迭代
我们开启了一个专门议题来讨论合并列车的下一步迭代,以便在合并列车流水线中实际开始运行测试。
启用运行“完整”测试流水线的合并列车所面临的挑战
为什么我们需要有一个“稳定”的默认分支?
如果默认分支不稳定(例如,默认分支的 CI/CD 流水线频繁失败),那么在出现故障的合并请求流水线之后添加的所有合并请求流水线都将不得不被取消并重新添加到列车中,如果合并列车很长,将会导致大量延迟。
默认分支需要多稳定?
我们没有具体的数字,但我们需要更好地掌握不稳定的测试失败和基础设施故障的数量(请参阅 Master Broken Incidents RCA Dashboard)。
为某些合并请求提供更快的反馈
修复损坏的 master
当你需要修复损坏的 master 时,可以添加 pipeline::expedited 标签来加快在合并请求上运行的流水线。
请注意,合并请求还需要设置 master:broken 或 master:foss-broken 标签。
回滚 MR
为了让你的回滚 MR 更快,请在创建合并请求之前使用 回滚 MR 模板。它将应用 pipeline::expedited 标签和其他标签,从而加快在合并请求上运行的流水线。
pipeline::expedited 标签
当分配此标签时,CI/CD 流水线的以下步骤将被跳过:
- e2e:test-on-omnibus-ee 任务。
- rspec:undercoverage 任务。
将此标签应用于合并请求,并为该 MR 运行一个新的流水线。
测试任务
我们为每个测试级别设有专门的任务,每个任务根据合并请求中所做的更改来运行。 如果你想无论更改如何都强制运行所有 RSpec 任务,可以将 pipeline:run-all-rspec 标签添加到合并请求中。
对于仅与文档相关的 MR,强制运行所有任务将不具备先决任务,从而导致错误。
端到端任务
有关更多信息,请参阅端到端测试流水线。
可观测性端到端任务
极狐GitLab 可观测性后端 有专门的端到端测试,会针对一个极狐GitLab 实例运行。这些测试旨在确保极狐GitLab 与可观测性后端之间的集成功能正常。
极狐GitLab 流水线有专门的任务(参见 observability-backend.gitlab-ci.yml),可以从极狐GitLab MR 执行。这些任务将触发可观测性后端流水线上的 E2E 测试,针对基于极狐GitLab MR 分支构建的实例运行。这些任务对于确保审核中的极狐GitLab 更改不会破坏可观测性后端流水线上的 E2E 测试非常有用。
有两个可观测性端到端任务:
- e2e:observability-backend-main-branch:针对极狐GitLab 可观测性后端的主分支执行测试。
- e2e:observability-backend:针对与 MR 分支同名的极狐GitLab 可观测性后端分支执行测试。
可观测性 E2E 任务仅对接触相关文件的合并请求自动触发,例如 lib/gitlab/observability/ 目录中的文件或与可观测性功能相关的特定配置文件。
要手动运行这些任务,你可以将 pipeline:run-observability-e2e-tests-main-branch 或 pipeline:run-observability-e2e-tests-current-branch 标签添加到你的合并请求中。
在以下示例工作流程中,开发人员创建了一个接触到可观测性代码并使用可观测性端到端任务的 MR:
- 开发人员创建一个触及可观测性代码的极狐GitLab MR。该 MR 会自动执行 e2e:observability-backend-main-branch 任务。
- 如果 e2e:observability-backend-main-branch 失败,这意味着要么 MR 破坏了某些东西(需要修复),要么 MR 所做的更改需要更新 E2E 测试。
- 要更新 E2E 测试,开发人员应该:
- 开发人员应该将 pipeline:run-observability-e2e-tests-current-branch 标签添加到极狐GitLab MR 上,并等待 e2e:observability-backend 任务成功。
- 如果 e2e:observability-backend 成功,开发人员可以合并这两个 MR。
此外,开发人员可以手动添加 pipeline:run-observability-e2e-tests-main-branch 来强制 MR 运行 e2e:observability-backend-main-branch 任务。这对于那些未被跟踪为与可观测性相关的文件更改可能很有用。
可能会有开发人员需要跳过这些测试的情况。要跳过测试:
- 对于 MR,应用 pipeline:skip-observability-e2e-tests label 标签。
- 对于整个项目,设置 CI 变量 SKIP_GITLAB_OBSERVABILITY_BACKEND_TRIGGER。
As-if-FOSS 任务和跨项目下游流水线
为了确保相关更改在 FOSS 项目中正常工作,在某些情况下我们还会运行:
- 同一流水线中的 * as-if-foss 任务
- 跨项目下游 FOSS 流水线
* as-if-foss 任务运行极狐GitLab 测试套件,就像是在 FOSS 环境中一样,即就像这些任务会在 gitlab-org/gitlab-foss 的上下文中运行。另一方面,跨项目下游 FOSS 流水线实际上是在 FOSS 项目内部运行的,这应该更接近实际的 FOSS 环境。
我们在以下情况下运行它们:
- 当合并请求上设置了 pipeline:run-as-if-foss 标签时
- 当合并请求上设置了 pipeline:as-if-foss-run-predictive 标签时(仅在 FOSS 流水线中运行预测性测试、RuboCop、eslint 和静态分析)
- 当合并请求在 gitlab-org/security/gitlab 项目中创建时
- 当 CI 配置文件发生更改时(例如,.gitlab-ci.yml 或 .gitlab/ci/**/*)
* as-if-foss 任务是在常规 EE 上下文任务之外额外运行的。它们设置了 FOSS_ONLY='1' 变量,并在测试开始运行之前移除 ee/ 文件夹。
跨项目下游 FOSS 流水线模拟将合并请求合并到 FOSS 项目中的默认分支,这会移除一个文件列表。该列表可以在 .gitlab/ci/as-if-foss.gitlab-ci.yml 和 merge-train/bin/merge-train 中找到。
其目的是确保在将 gitlab-org/gitlab 同步到 gitlab-org/gitlab-foss 之后,更改不会引入失败。
项目变量中设置的令牌
- AS_IF_FOSS_TOKEN:这是一个 极狐GitLab FOSS 项目令牌,具有 developer 角色和 write_repository 权限,用于推送生成的 as-if-foss/* 分支。
- 请注意,对于安全项目,相同名称应使用来自安全 FOSS 项目的其他令牌,这样我们就不会将安全更改推送到公共项目。
As-if-JH 跨项目下游流水线
它是什么
该流水线也称为 极狐(JiHu)验证流水线,目前允许失败。当这种情况发生时,请遵循验证流水线失败时该怎么做。
我们如何运行它
start-as-if-jh 作业会触发一个跨项目的下游流水线,该流水线会"模拟极狐"运行极狐GitLab测试套件,即模拟在 极狐GitLab JH 的上下文中运行流水线。这些作业仅在以下情况下创建:
- 当对功能标志进行更改时
- 当合并请求上设置了 pipeline:run-as-if-jh 标签时
此流水线在 极狐GitLab JH 验证 项目的一个生成分支的上下文中运行,该项目是 极狐GitLab JH 镜像 的一个镜像。
生成的分支名称以 as-if-jh/ 为前缀,后面紧跟合并请求中的分支名称。此生成的分支基于合并请求分支,在此基础上,还会从 对应的 JH 分支 下载更改并添加到顶部,使整个流水线"模拟极狐"。
其目的是确保在 极狐GitLab 同步到 极狐GitLab JH 后,更改不会引入失败。
何时考虑应用 pipeline:run-as-if-jh 标签
如果一个 Ruby 文件被重命名,并且存在对应的 prepend_mod 行,那么极狐GitLab JH 很可能依赖它,并需要相应的更改来重命名它正在前置的模块或类。
对应的 JH 分支
你可以在 极狐GitLab JH 上创建一个对应的 JH 分支,方法是在分支名称后追加 -jh。如果找到了对应的 JH 分支,as-if-jh 流水线会从相应的分支获取文件,而不是从默认分支 main-jh。
目前,CI 会尝试在 极狐GitLab JH 镜像 上获取分支,因此新的 JH 分支传播到镜像可能需要一些时间。
虽然 极狐GitLab JH 验证 是 极狐GitLab JH 镜像 的一个镜像,但它除了默认的 main-jh 外不包含任何对应的 JH 分支。这就是为什么当我们想获取对应的 JH 分支时,应该从主镜像获取,而不是从验证项目获取。
as-if-jh 流水线是如何配置的
整个过程如下所示:
我们仅在依赖项发生更改时运行 sync-as-if-jh-branch。
Rendering chart...
在项目变量中设置的令牌
- ADD_JH_FILES_TOKEN:这是一个 极狐GitLab JH 镜像 项目令牌,具有 read_api 权限,用于下载极狐文件。
- AS_IF_JH_TOKEN:这是一个 极狐GitLab JH 验证 项目令牌,具有 developer 角色和 write_repository 权限,用于推送生成的 as-if-jh/* 分支。
我们如何生成 as-if-jh 分支
首先,add-jh-files 作业将从对应的 JH 分支下载所需的极狐文件,并保存为产物。接下来,prepare-as-if-jh-branch 作业将从合并请求分支创建一个新的分支,提交更改,最后将分支推送到 验证项目。
可选地,如果合并请求对依赖项有更改,我们有一个额外的步骤来运行 sync-as-if-jh-branch 作业,以在验证项目中的 as-if-jh-code-sync 分支 上触发下游流水线。此作业将执行与 极狐代码同步 相同的过程,确保在运行验证流水线之前,可以将依赖项更改引入到 as-if-jh 分支。
如果没有依赖项更改,我们不运行此过程。
我们如何触发和运行 as-if-jh 流水线
在准备好 as-if-jh/* 分支并可选地同步之后,start-as-if-jh 作业将在 验证项目 中触发一个流水线,以运行跨项目的下游流水线。
极狐GitLab JH 镜像项目是如何设置的
极狐GitLab JH 镜像 项目是私有的,并且 CI 已禁用。
它是一个从 极狐GitLab JH 拉取的拉取镜像,会镜像所有分支,覆盖有分歧的引用,并且在镜像更新时不触发流水线。
拉取用户是 @gitlab-jh-validation-bot,他是该项目的维护者。凭据可以在 1password 工程保险库中找到。
因为极狐GitLab JH 是一个公开项目,所以镜像时未使用密码。
极狐GitLab JH 验证项目是如何设置的
这个 极狐GitLab JH 验证 项目是公开的,并且 CI 已启用,同时设置了临时的项目变量。
它是一个从 极狐GitLab JH 镜像 拉取的拉取镜像,会镜像特定分支:(master|main-jh),覆盖有分歧的引用,并且在镜像更新时不触发流水线。
拉取用户是 @gitlab-jh-validation-bot,他是该项目的维护者,同时也是 极狐GitLab JH 镜像 的维护者。凭据可以在 1password 工程保险库中找到。
一个来自 @gitlab-jh-validation-bot、具有 write_repository 权限的个人访问令牌被用作密码,以从极狐GitLab JH 镜像拉取更改。用户名设置为 gitlab-jh-validation-bot。
还有一个 流水线计划 用于运行维护流水线,其变量 SCHEDULE_TYPE 设置为 maintenance,每天运行,以更新缓存。
默认的 CI/CD 配置文件也设置在 jh/.gitlab-ci.yml,因此它的运行方式与 极狐GitLab JH 完全一样。
此外,一个特殊分支 as-if-jh-code-sync 已设置并受到保护。维护者可以推送,开发者可以为此分支合并。我们需要这样设置以便开发者可以合并,因为我们需要让开发者为这个分支触发流水线。在 解决具有开发者级别权限的用户无法再在受保护分支上运行流水线的问题 之前,这是一个妥协。
它用于运行 sync-as-if-jh-branch,以在合并请求更改了依赖项时同步依赖项。其实现请参见 我们如何生成 as-if-jh 分支。
极狐GitLab JH 验证项目的临时变量
- BUNDLER_CHECKSUM_VERIFICATION_OPT_IN 设置为 false
- 在极狐提交了 jh/Gemfile.checksum 之后,我们可以移除此变量。更多背景信息请参见:将其设置为 false 以跳过它
为什么我们同时有镜像项目和验证项目?
我们有几个原因来分开设立项目。
-
安全:以前,我们只有镜像项目。然而,为了完全缓解一个 安全问题,我们必须将镜像项目设为私有。
-
隔离:我们希望在一个完全隔离和独立的项目中运行 JH 代码。我们不应该在镜像项目所在的 gitlab-org 组下运行它。验证项目是完全隔离的。
-
成本:我们不希望每个合并请求都连接到 JiHuLab.com。将代码从 JiHuLab.com 镜像到 JihuLab.com 的某个地方,并让我们的合并请求从那里获取代码,这样更具成本效益。这意味着验证项目可以从镜像获取代码,而不是从 JiHuLab.com 获取。镜像项目将定期从 JiHuLab.com 获取。
-
分支分离/安全/效率:我们希望镜像所有分支,以便可以从 JiHuLab.com 获取对应的 JH 分支。然而,我们不希望覆盖验证项目中的 as-if-jh-code-sync 分支,因为我们用它来控制验证流水线,并且它可以访问 AS_IF_JH_TOKEN。但是,我们不能镜像除了某一个之外的所有分支。详情请参见 此议题。
鉴于此问题,验证项目设置为只镜像 master 和 main-jh。从技术上讲,我们甚至不需要这些分支,但我们确实希望使用所有默认分支来保持仓库最新,这样当我们从合并请求推送更改时,我们只需要推送合并请求的更改,这可能更有效率。
-
关注点分离:
- 验证项目只有以下分支:
- master 和 main-jh 以保持更改最新。
- as-if-jh-code-sync 用于依赖项同步。我们永远不应该镜像这个。
- 来自合并请求的 as-if-jh/* 分支。我们永远不应该镜像这些。
- 镜像项目的所有分支都来自 JiHuLab.com。我们从不向镜像项目推送任何东西,它也不运行任何流水线。CI/CD 在镜像项目中是禁用的。
- 验证项目只有以下分支:
我们可以考虑合并这两个项目以简化设置和流程,但我们需要确保所有这些原因都不再是问题。
rspec:undercoverage 作业
rspec:undercoverage 作业运行 undercover 来检测合并请求中引入的任何更改是否没有测试覆盖,如果没有则会失败。
rspec:undercoverage 作业从 rspec:coverage 作业获取覆盖率数据。
如果 rspec:undercoverage 作业检测到由于基础版方法在企业版中被覆盖而导致覆盖率缺失,请将 pipeline:run-as-if-foss 标签添加到合并请求,并启动一个新的流水线。
在紧急情况下,或者此作业出现误报时,请将 pipeline:skip-undercoverage 标签添加到合并请求,以允许此作业失败。
排查 rspec:undercoverage 失败问题
首先,检查 rspec:coverage 作业中 gitlab.lcov 产物的覆盖率数据。 rspec:coverage 作业可能由于各种 原因 未能收集到覆盖率数据。
rspec:undercoverage 作业可能会检测到一个方法调用的覆盖不足,但不显示 警告。
ruby1loc: app/controllers/projects/attestations_controller.rb:84:102, coverage: 87.5% 2 def parsed_attestation_file hits: n/a 3 @parsed_attestation_file ||= begin hits: 52 4 if attestation && attestation_file hits: 12 branches: 1/1 5 Gitlab::Json.parse(attestation_file.read) hits: 10 6 else hits: n/a 7 {} hits: 2 8 end hits: n/a 9 rescue JSON::ParserError => e hits: n/a 10 Gitlab::AppJsonLogger.error( hits: 2 11 message: 'Failed to parse attestation file', hits: n/a 12 error_class: e.class.name, hits: n/a 13 error_message: e.message, hits: n/a 14 attestation_id: attestation&.id, hits: n/a 15 project_id: project.id, hits: n/a 16 feature_category: 'artifact_security' hits: n/a 17 ) hits: n/a 18 {} hits: 2 19 end hits: n/a 20 end hits: n/a
rspec:undercoverage 作业存在 已知的缺陷,可能导致误报失败。如果你正在更新过旧的数据库迁移,也可能发生此类误报失败。 你可以在本地测试覆盖率,以确定是否可以安全地应用 pipeline:skip-undercoverage。例如,使用 <spec> 作为导致失败的测试名称:
- 运行 RUN_ALL_MIGRATION_TESTS=1 SIMPLECOV=1 bundle exec rspec <spec>。
- 运行 scripts/undercoverage。
如果这些命令返回 undercover: ✅ No coverage is missing in latest changes,那么你可以应用 pipeline:skip-undercoverage 来绕过流水线失败。
如果你必须使用 pipeline:skip-undercoverage 来绕过无关的流水线失败,请创建一个后续的 MR 来添加所需的测试覆盖率。这会让系统为其他人保持在一个更好的状态。
pajamas_adoption 作业
版本历史
pajamas_adoption 作业在合并请求中运行 Pajamas 采用扫描器,以防止 Pajamas 设计系统 的采用率出现倒退。
如果扫描器检测到合并请求导致倒退,该作业将失败。如果这些倒退无法在合并请求中修复,请将 pipeline:skip-pajamas-adoption 标签添加到合并请求,然后重试该作业。
测试套件并行化
我们当前的 RSpec 测试并行化设置如下:
- prepare 阶段的 retrieve-tests-metadata 作业确保我们有一个 knapsack/report-master.json 文件:
- knapsack/report-master.json 文件是从运行 update-tests-metadata 的最新 main 流水线(目前是每 2 小时一次的 maintenance 计划 master 流水线)获取的,如果它不在这里,我们用 {} 初始化该文件。
- 每个 [rspec|rspec-ee] [migration|unit|integration|system|geo] n m 作业都使用 knapsack rspec 运行,并且应该均匀地分担测试:
- 它之所以有效,是因为这些作业可以访问 knapsack/report-master.json,因为"来自所有先前阶段的产物会默认传递"。
- 这些作业将它们自己的报告路径设置为 "knapsack/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json"。
- 如果 knapsack 正在完成它的工作,运行的测试文件应该列在 Report specs 下,而不是 Leftover specs 下。
- update-tests-metadata 作业(仅在 规范项目 的预定流水线上运行,并以两种方式更新 knapsack/report-master.json:
- 默认情况下,它会获取所有 knapsack/rspec*.json 文件,并将它们全部合并到一个单独的 knapsack/report-master.json 文件中,并保存为产物。
- (实验性)当 AVERAGE_KNAPSACK_REPORT 环境变量设置为 true 时,该作业不会合并报告,而是计算 knapsack/report-master.json 和 knapsack/rspec*.json 之间测试持续时间的平均值,以减少来自潜在随机因素(如 spec 顺序、运行器硬件差异、不稳定的测试等)对性能的影响。 这种实验性方法旨在更好地预测每个 spec 文件的持续时间,以更均匀地在并行作业之间分配负载,从而使作业能在大致相同的时间完成。
之后,下一个流水线将使用最新的 knapsack/report-master.json 文件。
代码覆盖率
我们从测试套件中收集代码覆盖率数据,以支持测试选择、覆盖率分析和不稳定的测试分析。
覆盖率类型
| 类型 | 收集方法 | 测试 |
|---|---|---|
| 后端 | SimpleCov → LCOV | RSpec |
| 后端 E2E | Coverband | E2E 规格 |
| 前端 | Istanbul | Jest |
| 前端 E2E | Istanbul | E2E 规格 |
| Workhorse | Go 覆盖率 | Go 测试 |
CI 作业
覆盖率数据流经几个 CI 作业:
-
收集:测试在覆盖率检测下运行
- rspec 作业通过 SimpleCov 收集后端覆盖率
- jest 作业通过 Istanbul 收集前端覆盖率
- e2e:test-on-gdk 通过 Coverband(后端)和 Istanbul(前端)收集 E2E 覆盖率
- workhorse 作业收集 Go 覆盖率
-
合并:来自并行作业和 E2E 的覆盖率被合并
- rspec:coverage 将 RSpec 覆盖率合并到 coverage/lcov/gitlab.lcov
- coverage-frontend 合并 Jest 覆盖率
- 合并脚本将 E2E 覆盖率与单元/集成覆盖率合并
-
导出:合并后的覆盖率被导出到 ClickHouse
- test-coverage:export-rspec-and-e2e 导出后端覆盖率
- test-coverage:export-jest-and-e2e 导出前端覆盖率
- test-coverage:export-workhorse 导出 Workhorse 覆盖率
有关覆盖率收集、数据流和 ClickHouse 存储的详细文档,请参阅 代码覆盖率。
不稳定的测试重试和隔离
自动重试失败的测试
失败的后端测试会在一个单独的 RSpec 进程中自动重试一次。这有助于检测不稳定的测试,即在相同的提交 SHA 下失败后又能通过的测试。
"在新的 RSpec 进程中重试失败的测试"功能可以通过将 $RETRY_FAILED_TESTS_IN_NEW_PROCESS 变量设置为 false 来禁用。
隔离的测试
极狐GitLab CI 流水线使用 快速隔离 来跳过那些在调查期间阻塞流水线的测试。
快速隔离过程可以通过将 $FAST_QUARANTINE 变量设置为 false 来禁用。
兼容性测试
默认情况下,我们使用 JihuLab.com 上运行的版本运行所有测试。
其他版本(通常是一个向后兼容版本和一个向前兼容版本)应该在每夜计划流水线中运行。
对此通用指南的例外情况应有充分理由并记录在案。
Ruby 版本测试
我们在 JihuLab.com 以及默认分支上运行 Ruby 3.3。为了为下一个 Ruby 版本做准备,我们在 Ruby 3.4 中运行合并请求。有关更多详细信息,请参阅 Ruby 3.4 史诗 中的路线图。
为了确保所有受支持的 Ruby 版本都能正常工作,我们还在专用的每 2 小时一次的计划流水线上为每个受支持的版本运行我们的测试套件。
对于合并请求,你可以添加以下标签来仅运行相应的 Ruby 版本:
- pipeline:run-in-ruby3_3
PostgreSQL 版本测试
我们的测试套件针对 PostgreSQL 16 运行,因为 JihuLab.com 在 PostgreSQL 16 上运行,并且 Omnibus 默认对新安装和升级使用 PG14。
我们在每夜计划流水线上针对 PostgreSQL 16、17 和 18 运行测试套件。
随着 PG17 的加入,我们已经接近每夜作业的限制,每个流水线最多 2000 个作业,目前已经达到 1946。 增加新的作业系列可能导致每夜流水线失败。
当前版本测试
| 位置 | PostgreSQL 版本 | Ruby 版本 |
|---|---|---|
| 合并请求 | 17 (默认版本) | 3.3 (默认版本) |
| master 分支提交 | 17 (默认版本) | 3.3 (默认版本) |
| master 分支的 maintenance 计划流水线(每双数小时 XX:05 运行) | 17 (默认版本) | 3.3 (默认版本) |
| ruby-next 分支的 maintenance 计划流水线(每单数小时 XX:10 运行) | 17 (默认版本) | 3.3 |
| master 分支的 nightly 计划流水线 | 17 (默认版本), 16 和 18 | 3.3 (默认版本) |
| master 分支的 weekly 计划流水线 | 17 (默认版本) | 3.3 (默认版本) |
对于我们正在测试的下一个 Ruby 版本,我们在 ruby-next 分支上每 2 小时运行一次维护计划流水线。ruby-next 不能有任何更改。该分支仅用于在计划维护流水线中使用另一个 Ruby 版本运行流水线。
ruby-sync 分支
ruby-sync 分支使 ruby-next 和 rails-next 分支与 master 保持同步。它是一个孤儿分支(非派生自 master),只包含它自己的 .gitlab-ci.yml、一个 scripts/slack 辅助脚本和一个 README.md。
一个 计划流水线 每 2 小时在 ruby-sync 上运行一次。gitlab 作业:
- 克隆完整的 gitlab-org/gitlab 仓库。
- 对于 ruby-next 和 rails-next 中的每一个:检出分支,合并 origin/master,并推送结果。
这些推送不会触发下游流水线。ruby-next 和 rails-next 分支独立运行它们自己的计划维护流水线。
认证
gitlab 作业使用一个项目令牌 (RUBY_SYNC_TOKEN) 进行认证,该令牌具有 write_repository 范围和 Maintainer 角色。该令牌存储在 ruby-sync 分支的流水线计划变量中。
重试和失败通知
gitlab 作业在脚本失败时重试一次以处理瞬态错误。如果仍然失败,一个 notify 作业会通过 CI_SLACK_WEBHOOK_URL 向 #backend Slack 频道(用户名 ruby-sync)发送一条消息:
plaintext☠️ ruby-sync 未能将 master 合并到 gitlab-org/gitlab 的 ruby-next/rails-next 分支中。流水线:<pipeline_url> — 文档:<docs_url>
排查 ruby-sync 失败问题
常见的失败原因:
- 瞬态极狐GitLab 负载错误:该作业克隆完整的仓库,在高负载期间可能会失败。重试失败的流水线。
- 合并冲突:如果 ruby-next 或 rails-next 与 master 存在差异,导致冲突,git merge 步骤会失败。在受影响的分支上手动解决冲突并重试。
- 认证错误:验证 RUBY_SYNC_TOKEN 项目令牌是否未过期。检查流水线计划配置。
要重试,请前往 流水线计划 页面并运行 ruby-sync 计划,或从流水线页面重试失败的作业。
Redis 版本测试
我们的测试套件针对 Redis 6 运行,因为 JihuLab.com 在 Redis 6 上运行,并且 Omnibus 默认对新安装和升级使用 Redis 6。
我们确实在 nightly 计划流水线上针对 Redis 7 运行我们的测试套件,特别是在运行向前兼容的 PostgreSQL 15 作业时。
当前版本测试
| 位置 | Redis 版本 |
|---|---|
| MR | 6 |
| 默认分支 (非计划流水线) | 6 |
| nightly 计划流水线 | 7 |
单一数据库测试
默认情况下,所有测试都使用 多个数据库 运行。
我们还在每夜计划流水线中以及触及数据库相关文件的合并请求中使用单一数据库运行测试。
单一数据库测试以两种模式运行:
- 具有一个连接的单一数据库。其中极狐GitLab 使用一个连接池连接到所有表。 这会运行所有以 -single-db 结尾的作业。
- 具有两个连接的单一数据库。其中极狐GitLab 使用不同的数据库连接分别连接到 gitlab_main、gitlab_ci 数据库表。 这会运行所有以 -single-db-ci-connection 结尾的作业。
如果你想强制测试以单一数据库运行,可以向合并请求添加 pipeline:run-single-db 标签。
Elasticsearch 和 OpenSearch 版本测试
我们的测试套件针对 Elasticsearch 9 运行,因为在满足特定条件时 JihuLab.com 运行在 Elasticsearch 9 上。
我们在夜间定时流水线中针对 Elasticsearch 8、9 以及 OpenSearch 1、2 运行测试套件。所有 测试套件都使用 PostgreSQL 17,因为数据库和搜索后端之间没有依赖关系。
| 位置 | Elasticsearch 版本 | OpenSearch 版本 | PostgreSQL 版本 |
|---|---|---|---|
| 带有标签 ~group::global search 或 ~pipeline:run-search-tests 的合并请求 | 9.X(生产环境) | 17(默认版本) | |
| 针对 master 分支的夜间定时流水线 | 7.X、9.X(生产环境) | 1.X、2.X | 17(默认版本) |
| 针对 master 分支的每周定时流水线 | 8.X | 最新 | 17(默认版本) |
监控
极狐GitLab 测试套件针对 main 分支以及任何名称中包含 rspec-profile 的分支进行监控。
日志记录
- 在 CI 中,默认禁用了 Rails 日志记录到 log/test.log,出于性能原因。 要覆盖此设置,请提供 RAILS_ENABLE_TEST_LOG 环境变量。
CI 配置内部细节
请参阅专门的 CI 配置内部细节页面。
性能
请参阅专门的 CI 配置性能页面。