> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-trino-dialect.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> ClickHouse 持续集成系统概览

# 持续集成（CI）

当你提交拉取请求时，ClickHouse 的[持续集成 (CI) 系统](/zh/resources/develop-contribute/contribute/tests#test-automation)会对你的代码运行一些自动化检查。
这会发生在仓库维护者 (ClickHouse 团队成员) 审查你的代码并为你的拉取请求添加 `can be tested` 标签之后。
如 [GitHub checks 文档](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-status-checks)所述，这些检查结果会显示在 GitHub 拉取请求页面上。
如果某项检查失败，你可能需要修复它。
本页概述了你可能会遇到的检查，以及相应的处理方法。

如果看起来检查失败与你的更改无关，可能只是暂时性故障或基础设施问题。
向该拉取请求推送一个空提交，以重新启动 CI 检查：

```shell theme={null}
git commit --allow-empty
git push
```

如果你不确定该怎么做，请向维护者求助。

<div id="merge-with-master">
  ## 与 master 合并
</div>

验证该拉取请求能否合并到 master。
如果不能，此检查将失败，并显示消息 `Cannot fetch mergecommit`。
要修复此检查，请按 [GitHub 文档](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/resolving-a-merge-conflict-on-github) 中的说明解决冲突，或者使用 git 将 `master` 分支合并到你的拉取请求分支。

<div id="docs-check">
  ## 文档检查 (Mintlify)
</div>

验证 Mintlify 文档、内部链接和锚点、重定向、代码片段导入及更新日志。外部链接检查失败将报告为警告。
该检查还会拒绝直接编辑生成区域和只读文档副本。请改为更新源注册中的结构化文档；有意更新生成内容时，必须添加 `pr-autogenerated-docs` 标签。
如果文档更改后检查失败，请打开报告并查找 `ERROR` 和 `WARNING` 消息。

<div id="description-check">
  ## 描述检查
</div>

检查你的拉取请求描述是否符合模板 [PULL\_REQUEST\_TEMPLATE.md](https://github.com/ClickHouse/ClickHouse/blob/master/.github/PULL_REQUEST_TEMPLATE.md)。
你必须为此次更改指定一个更新日志类别 (例如 Bug Fix) ，并为 [CHANGELOG.md](/zh/resources/changelogs/oss/2026) 编写一条面向用户的变更说明

<div id="docker-image">
  ## Docker 镜像
</div>

构建 ClickHouse server 和 Keeper 的 Docker 镜像，以验证其能否正确构建。

<div id="official-docker-library-tests">
  ### 官方 Docker 库测试
</div>

运行[官方 Docker 库](https://github.com/docker-library/official-images/tree/master/test#alternate-config-files)中的测试，以验证 `clickhouse/clickhouse-server` Docker 镜像 能否正常工作。

要添加新测试，请创建目录 `ci/jobs/scripts/docker_server/tests/$test_name`，并在其中创建 `run.sh` 脚本。

有关这些测试的更多信息，请参见 [CI 作业脚本文档](https://github.com/ClickHouse/ClickHouse/tree/master/ci/jobs/scripts/docker_server)。

<div id="marker-check">
  ## Marker 检查
</div>

此检查表示 CI 系统已开始处理该拉取请求。
当其状态为“pending”时，表示并非所有检查都已开始运行。
当所有检查都开始运行后，其状态会变为“success”。

<div id="style-check">
  ## 风格检查
</div>

对代码库执行各种风格检查。下面的每个子检查都对应 [`ci/jobs/check_style.py`](https://github.com/ClickHouse/ClickHouse/blob/master/ci/jobs/check_style.py) 中的一个 `testname`，并且都可以使用 `--test <name>` 单独运行 (见下文) 。

<div id="cpp">
  ##### cpp
</div>

通过 [`check_cpp.sh`](https://github.com/ClickHouse/ClickHouse/blob/master/ci/jobs/scripts/check_style/check_cpp.sh) 进行基于 Regex 的 C++ 风格检查。如果检查失败，请根据[代码风格指南](/zh/resources/develop-contribute/contribute/style)修复相应的风格问题。

<div id="whitespace-check">
  ##### whitespace\_check
</div>

标记 C++ 中逗号后不用于列对齐的双空格。

<div id="catch-all">
  ##### catch\_all
</div>

禁止在析构函数、`main` 和 fuzzer 入口点以外使用 `catch (...)`，因为在其他位置吞掉未知异常并不安全。

<div id="yamllint">
  ##### yamllint
</div>

使用 `.yamllint` 检查 `.github/` 下的 YAML 工作流文件。

<div id="xmllint">
  ##### xmllint
</div>

对 `tests/` 和 `programs/` 下的 XML 文件进行验证。

<div id="functional-tests-check">
  ##### functional\_tests\_check
</div>

检查无状态测试：对 `event_date` 进行过滤的查询必须使用 `>= yesterday()`，而不能使用 `today()` (以避免在午夜前后出现不稳定情况) ；此外，测试文件名不得包含 `fail`。

<div id="test-numbers-check">
  ##### test\_numbers\_check
</div>

标记无状态测试编号中较大的空缺 (`tests/queries/0_stateless/<NNNNN>_*`) 。

<div id="symlinks">
  ##### 符号链接
</div>

检测仓库中的失效符号链接。

<div id="various">
  ##### 其他
</div>

通过 [`various_checks.sh`](https://github.com/ClickHouse/ClickHouse/blob/master/ci/jobs/scripts/check_style/various_checks.sh) 执行的各项仓库检查：对 `system.query_log` / `system.parts` / 等的查询必须按 `currentDatabase` 过滤，`Replicated*MergeTree` 的 ZooKeeper path 必须包含每个测试各自的前缀，集成测试目录必须包含 `__init__.py`，不得包含 UTF BOM，源码/数据文件不得设置可执行位，第三方 docker-compose image 不得使用 `:latest` 标签，等等。

<div id="running-style-check-locally">
  ### 在本地运行样式检查作业
</div>

整个 *风格检查* 作业都可以通过以下方式在 Docker 容器中于本地运行：

```sh theme={null}
python -m ci.praktika run "Style check"
```

要运行某项特定检查 (例如 *cpp* 检查) ：

```sh theme={null}
python -m ci.praktika run "Style check" --test cpp
```

这些命令会拉取 `clickhouse/style-test` Docker 镜像，并在容器化环境中运行该作业。
除 Python 3 和 Docker 外，无需其他依赖。

<div id="running-stateless-tests">
  ## 运行无状态测试
</div>

本地安装并使用默认设置的 ClickHouse 可能适用于某些特定测试用例，但无法正确运行所有测试查询。在 CI 中，每个作业都会部署特定的 ClickHouse 配置 (例如 S3 存储、并行副本) ，手动复现这些配置可能相当繁琐。为避免这种情况，你可以在本地使用与 CI 相同的编排方式复现任何 CI 作业——无需手动配置。

<div id="ci-prerequisites">
  #### 前置条件
</div>

* Python 3 (仅需标准库)
* Docker

如有需要，请在 Ubuntu 上安装 Docker 并重新登录：

```sh theme={null}
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker "$USER"
sudo tee /etc/docker/daemon.json <<'EOF'
{
  "ipv6": true,
  "ip6tables": true
}
EOF
sudo systemctl restart docker
```

<div id="run-ci-job-locally">
  #### 在本地运行 CI 作业
</div>

从 CI 报告中任选一个作业名称，然后在本地运行：

```bash theme={null}
python -m ci.praktika run "<JOB_NAME>"
```

* 始终按 CI 报告中的显示结果原样引用作业名称 (其中可能包含空格和逗号) ，例如：`"Stateless tests (amd_debug, parallel)"`。这样会使用与 CI 相同的 ClickHouse 配置，并运行相同的测试。
* 作业名称中的架构和构建类型 (例如 `amd_debug`) 是 CI 专用标记。在本地运行时，它们不会产生任何影响——作业会使用你提供的二进制文件，以及你当前所在架构。作业名称只决定 ClickHouse 配置和测试集 (除非用 `--test` 覆盖) 。
* 在 CI 中，功能测试会拆分成多个批次，以便更高效地利用资源。例如，`"Stateless tests (amd_debug, parallel)"` 和 `"Stateless tests (amd_debug, sequential)"` 合起来覆盖完整范围：可安全并行运行的测试会并发执行，其余测试则顺序执行。这种拆分通过尽可能提高并行度来缩短 CI 总耗时。若要在本地复现完整测试范围，请同时运行这两个批次。
* 此外还有一个 `"Fast test"` CI 作业，它会运行一小部分功能测试，以验证 ClickHouse 的基础功能——它使用的是不包含全部可选模块的构建，也是发现回归问题最快的方法。你也可以用同样的方式在本地运行它。将你的 ClickHouse 二进制文件放到默认搜索路径之一 (`./ci/tmp/clickhouse`、`./build/programs/clickhouse` 或 `./clickhouse`) ——否则该作业会先尝试构建 ClickHouse：
  ```bash theme={null}
  python -m ci.praktika run "Fast test"
  ```

<div id="run-specific-tests-within-ci-job">
  #### 在 CI 作业中运行指定测试
</div>

使用 `--test` 时，该作业会准备一套与 CI 中使用的完全相同的 ClickHouse 配置，但只运行选定的测试：

```bash theme={null}
python -m ci.praktika run "Stateless tests (amd_debug, parallel)" \
  --test 00001_select1
```

* 你可以传入多个测试名称：
  ```bash theme={null}
  python -m ci.praktika run "Stateless tests (amd_debug, parallel)" \
    --test 00001_select1 00002_log_and_exception_messages_formatting
  ```
* 提示：如果任意 ClickHouse 配置都可以，只是需要运行特定测试，请使用别名 `functional`，而不是完整的 job 名称：
  ```bash theme={null}
  python -m ci.praktika run functional --test 00001_select1
  ```

<div id="additional-customization-options">
  #### 其他自定义选项
</div>

* `--path PATH` — ClickHouse 二进制文件的自定义路径。默认情况下，运行器会按顺序在以下位置查找：`./ci/tmp/clickhouse`、`./build/programs/clickhouse`、`./clickhouse`。
* `--count N` — 将每个测试重复执行 N 次。
* `--workers N` — 覆盖根据机器容量自动计算出的并行工作线程数。

<div id="build-check">
  ## 构建检查
</div>

以多种配置构建 ClickHouse，供后续步骤使用。

<div id="running-builds-locally">
  ### 在本地运行构建
</div>

可以通过以下方式，在类似 CI 的环境中于本地运行该构建：

```bash theme={null}
python -m ci.praktika run "<BUILD_JOB_NAME>"
```

除 Python 3 和 Docker 之外，无需其他依赖。

<div id="available-build-jobs">
  #### 可用的构建作业
</div>

构建作业名称与 CI Report 中显示的名称完全一致：

**AMD64 构建：**

* `Build (amd_debug)` - 带符号的调试构建
* `Build (amd_release)` - 优化后的发布构建
* `Build (amd_asan)` - Address Sanitizer 构建
* `Build (amd_tsan)` - Thread Sanitizer 构建
* `Build (amd_msan)` - Memory Sanitizer 构建
* `Build (amd_ubsan)` - Undefined Behavior Sanitizer 构建
* `Build (amd_binary)` - 不使用 Thin LTO 的快速发布构建
* `Build (amd_compat)` - 适用于旧系统的兼容性构建
* `Build (amd_musl)` - 使用 musl libc 的构建
* `Build (amd_darwin)` - macOS 构建
* `Build (amd_freebsd)` - FreeBSD 构建

**ARM64 构建：**

* `Build (arm_release)` - ARM64 优化后的发布构建
* `Build (arm_asan)` - ARM64 Address Sanitizer 构建
* `Build (arm_coverage)` - 带覆盖率插桩的 ARM64 构建
* `Build (arm_binary)` - 不使用 Thin LTO 的 ARM64 快速发布构建
* `Build (arm_darwin)` - macOS ARM64 构建
* `Build (arm_v80compat)` - ARMv8.0 兼容性构建

**其他架构：**

* `Build (ppc64le)` - PowerPC 64 位小端
* `Build (riscv64)` - RISC-V 64 位
* `Build (s390x)` - IBM System/390 64 位
* `Build (loongarch64)` - LoongArch 64 位
* `Build (wasm64)` - 通过 Emscripten 构建的 WebAssembly 64 位。Experimental：构建 `clickhouse` 二进制文件，并验证 `clickhouse local` 可在 Node.js ≥ 24 下执行查询 (该模块也可在浏览器中运行，但 CI 尚未对此进行验证)

如果作业成功，构建结果将位于 `<repo_root>/ci/tmp/build` 目录下。

**注意：** 对于不属于“其他架构”类别的构建 (“其他架构”类别使用交叉编译) ，你的本地机器架构必须与构建类型一致，才能按 `BUILD_JOB_NAME` 的要求生成相应构建。

<div id="example-run-local">
  #### 示例
</div>

要运行本地调试版本：

```bash theme={null}
python -m ci.praktika run "Build (amd_debug)"
```

如果上述方法不适合你，请使用构建日志中的 cmake 选项，并遵循[通用构建流程](/zh/resources/develop-contribute/build/build)。

<div id="functional-stateless-tests">
  ## 无状态功能测试
</div>

运行针对以不同配置构建的 ClickHouse 二进制文件的[无状态功能测试](/zh/resources/develop-contribute/contribute/tests#functional-tests)，包括 release、debug、启用 sanitizers 等。
查看报告，确认哪些测试失败，然后按[此处](/zh/resources/develop-contribute/contribute/tests#functional-tests)的说明在本地复现。
请注意，复现时必须使用正确的构建配置——某项测试可能在 AddressSanitizer 下失败，但在 Debug 下通过。
从 [CI build checks page](/zh/get-started/setup/self-managed/advanced) 下载二进制文件，或在本地自行构建。

在拉取请求中，大多数名称以 `selected tests` 结尾的 sanitizer 作业不会运行整个测试套件。
它们只运行针对变更选定的测试：拉取请求新增或修改的测试、该拉取请求中已失败的测试，以及根据覆盖率数据库覆盖已变更代码行的测试。
MSan/WasmEdge 作业仍会运行完整测试套件，因为现有的 Wasm UDF 测试与 MSan 不兼容。完整测试套件也会在 debug 和普通二进制配置中运行；启用 sanitizers 的构建则通过[压力测试](#stress-test)进行测试，master 分支则会在每种配置下运行完整测试套件。

<div id="integration-tests">
  ## 集成测试
</div>

运行[集成测试](/zh/resources/develop-contribute/contribute/tests#integration-tests)。

<div id="bugfix-validate-check">
  ## Bugfix validate 检查
</div>

检查是否新增了测试 (功能测试或集成测试) ，或者是否有修改过的测试在基于 master 分支构建的 二进制文件 上运行失败。
当拉取请求带有 "pr-bugfix" 标签时，会触发此检查。

<div id="stress-test">
  ## 压力测试
</div>

从多个客户端并发运行无状态功能测试，以发现并发相关的错误。如果失败：

* 请先修复所有其他测试失败；
  * 查看报告，找到服务器日志，并检查其中可能的错误原因。

<div id="compatibility-check">
  ## 兼容性检查
</div>

检查 `clickhouse` 二进制文件能否在使用旧版 libc 的发行版上运行。
如果失败，请向维护者寻求帮助。

<div id="ast-fuzzer">
  ## AST fuzzer
</div>

运行随机生成的查询，用于捕获程序错误。
如果运行失败，请向维护者寻求帮助。

<div id="performance-tests">
  ## 性能测试
</div>

用于衡量查询性能的变化。
这是耗时最长的一项检查，运行时间接近 6 小时。
有关性能测试报告的详细说明，请参见[此处](https://github.com/ClickHouse/ClickHouse/blob/master/tests/performance/scripts/README.md#how-to-read-the-report)。

<div id="revert-ci-regressions">
  ## 回退 CI 回归问题
</div>

这不是针对你的拉取请求运行的检查：它每小时在 `master` 上运行，可能会回退已合并的拉取请求。

该作业会获取 CI 数据库在过去 24 小时内为 `master` 记录的失败测试，并按测试名称分组，涵盖该测试失败过的所有检查。
同一测试在 debug 和 tsan 构建中失败，视为一次具有单一待查原因的失败；该测试出现过的检查会作为证据纳入调查：破坏某项测试的更改通常会同时导致它在多个构建中失败。
未归属于任何测试的失败，例如构建失败或超时的作业，会被排除：“为什么这个检查失败”并没有可据以回退的单一答案。
测试工具以类似测试的名称记录整个脚本情况的行，例如 `Test script failed` 或 `Server died`，也会同样被排除。
在多个 `master` 提交中失败的测试会交给 AI 智能体处理；该智能体可访问包含完整 `master` 历史记录的仓库，以及 CI 数据库的只读权限，并回答一个问题：该失败是否由最近合并的拉取请求引入，以及是哪一个。
该智能体不持有 GitHub 凭据，也无法获取凭据 -- 它以独立的非特权用户身份运行，环境为空，并且该用户对云凭据端点的访问已被防火墙阻断 -- 它在仓库的一次性 clone 中工作，而非作业自身的 checkout 中，因此其得出的任何结论 -- 以及可能遗留的任何内容 -- 都无法到达 GitHub，除非通过下方的检查。
阈值按提交而非失败行计数，因此，一个在三个构建中失败的错误提交仍只算一次，不会被处理。
它还会按失败模式分别计数：记录的输出会生成指纹，并将易变部分 (地址、时间戳、随机数据库名称) 归一化移除；名称对应两种不同原因的测试 -- 一个提交上的回归问题和另一个提交上无关的偶发失败 -- 不会被视为重复失败，因此在某一原因单独重复之前，不会进行任何调查。

只有明确无歧义的答案才会触发操作。
当智能体以高置信度报告回归问题，且指定的拉取请求通过安全检查 (在过去三天内合并到 `master`、本身不是回退、尚未被回退，并且该回退可以干净地应用) 时，该作业会将其回退，立即合并回退而不等待检查，并创建一个标题为 `Reapply "..."` 的草稿拉取请求来重新引入该更改。
回归问题判定必须同时指明拉取请求及其引入时对应的 `master` 提交，且两者必须一致：该作业会根据 GitHub 中该拉取请求所产生的合并提交记录核对编号；若二者不一致，则不会采取任何操作。
失败消失后便不会再回退：失败停止后仍会在观察窗口中保留整整一天，因此作业会在回退前再次查询 CI 数据库；如果所有受影响的检查都已在最新的 `master` 提交上运行过，而其中未出现该失败，则会将其记录为已修复并不作处理。
最新提交以分支自身的历史为准，而不是以检查运行的时间为准 -- 一个检查启动较晚的旧提交绝不能被视为新的绿色证据。
采用“未出现”而非“通过”，因为该作业调查的大多数问题没有可供查找的通过行：逻辑错误或卡住的检查会以失败本身的文本记录，并且仅在其发生时记录。
只有某个检查的一次运行完成了其测试，才算该检查已在某个提交上执行过测试：中途终止的运行 -- 工具会将其记录为 `Test script failed` 或 `Server died`，并与实际产生的测试行并列 -- 只运行了*部分*测试，未必包括这个测试；它没有报告该失败并不能作为证据，而同一检查在同一提交上完成的重新运行则可以。
需要多少次未出现才算数取决于失败发生的频率 -- 对于每百次运行才失败一次的问题，几次正常提交没有意义，因此要求未出现的持续时间超过该失败记录中自身两次发生之间最长的沉寂期。
当问题根本无法回答时 -- 记录到该失败的某个检查不再以该名称报告，或自失败开始以来的提交历史超过查询返回的范围 -- 也会记录该情况，且不会执行回退。
每次运行最多回退两个拉取请求。

如果你的拉取请求被回退：

* 回退拉取请求会说明失败内容，以及为何将责任归于该更改。如果归因错误，请在那里说明并重新引入该更改。
* `Reapply "..."` 草稿拉取请求会原样保留你的更改。在该分支上修复失败，将其标记为准备审查，然后让它通过正常的 CI。

每次调查都会记录在 CI 数据库的 `checks_investigated` 表中，包括未回退任何内容的调查。
这些值会按其在 `checks` 中记录的原样保留，因此这两个表可以关联起来——直接通过 `test_name` 关联；对于将多个 `checks` 行汇集到数组中的列，则使用 `has(check_names, check_name)` 和 `has(commit_shas, commit_sha)`；对于被归责的拉取请求，则使用 `offending_pull_request_number = pull_request_number`——作业检查了什么、得出了什么结论以及采取了什么操作的历史记录，均可在 [play.clickhouse.com](https://play.clickhouse.com/) 上查询：

```sql theme={null}
SELECT investigation_time, test_name, check_names, failure_count, commit_count, verdict, confidence, action, explanation
FROM checks_investigated
WHERE investigation_time >= now() - INTERVAL 7 DAY
ORDER BY investigation_time DESC;
```

该作业在 `ci/jobs/revert_ci_regressions.py` 中实现，并作为 `Hourly` 工作流的一部分运行。
使用 `--dry-run` 运行时，会检查并评估所有保护条件，但不会做任何更改：不会创建表、写入行、创建分支、拉取请求或合并；原本将写入的行会改为打印输出。
另一个工作流 `.github/workflows/revert_broken_prs.yml` 会回退在自身 CI 失败期间合入的合并；两者均使用相同的 `revert-<pull request number>` 分支名称，因此不会对同一拉取请求执行两次回退。
手动发起的回退也会被考虑在内：如果回退已在 `master` 上、已存在名为 `revert-<pull request number>` 或 `revert-<pull request number>-<branch>` (GitHub 上的 `Revert` 按钮会创建此类名称) 的分支，或者来自此类分支的拉取请求处于打开或已合并状态，该作业就会停止执行。
