Apache Arrow Crossbow 打包与集成测试系统:架构原理与 arc 实战指南
【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow
Apache Arrow 的 Crossbow 是一套基于 Git 分支作为任务队列的持续集成调度系统,它把"打包"(conda 包、Python Wheel、Linux 发行版包、Homebrew Formula 等)和"集成测试"(Pandas、Dask、Spark、HDFS、Turbodbc 等)自动化为可复现、可追踪的构建任务。本文以仓库文档 crossbow.rst 为骨架,结合dev/tasks与dev/archery下的真实实现,完整讲解 Crossbow 的安装、提交、状态查询、产物下载与报告机制,并深入剖析其"Git 分支即任务队列"的调度原理,帮助你像 Arrow 维护者一样按需触发任意平台的构建与测试。
Crossbow 是什么:dev/tasks目录的使命
Crossbow 的出发点位于arrow/dev/tasks目录,它承载了 Apache Arrow 打包与集成测试的全套自动化。按功能划分,它主要覆盖两大类别:
打包(Packages)
- C++ 与 Python 的 conda-forge 包:覆盖 Linux、macOS、Windows;
- Python Wheel:覆盖 Linux(manylinux / musllinux)、macOS、Windows;
- C++ 与 GLib 的 Linux 发行版包:面向多个发行版(apt、yum 体系);
- Java(面向 Gandiva)相关构建。
集成测试(Integration tests)
- 各类 Docker 容器测试(C++、Python、R、Ruby、GLib 等镜像);
- Pandas、Dask、Turbodbc、HDFS、Spark 的兼容性测试。
这些任务并不是"一次性脚本",而是以结构化配置存在:任务定义集中在 dev/tasks/tasks.yml,CI 工作流模板分散在dev/tasks下的子目录(如 python-wheels、docker-tests、verify-rc、r 等),真正的调度逻辑则由archery crossbow子命令实现,代码位于 dev/archery/archery/crossbow/。
架构总览:Executor、Queue 与 Scheduler
Crossbow 的架构可以拆成三个角色:
Executors:公网 CI 执行器
单个构建任务最终在公共 CI 服务上执行,当前仓库所依赖的执行器组合为:
- Linux:GitHub Actions、Travis CI、Azure Pipelines;
- macOS:GitHub Actions、Azure Pipelines;
- Windows:GitHub Actions、Azure Pipelines。
从 tasks.yml 的实际任务看,绝大多数任务标注为ci: github,即默认走 GitHub Actions;Travis、CircleCI、Azure 的配置模板(.travis.yml、.circleci/config.yml)在 core.py 中仍作为"跳过分支"的默认骨架保留,用于让不参与某次构建的 CI 服务自动跳过对应分支。
Queue:作为任务队列的 Git 仓库
由于 CI 服务按"分支推送"触发,Crossbow 把调度抽象成一个额外的 Git 仓库——队列仓库(Queue Repository),任何人都可以托管一个队列仓库(惯例命名为<ghuser>/crossbow)。
在 Crossbow 中,"一个任务"本质上就是队列仓库里的一个 Git 提交:它位于某个特定分支上,分支内包含触发对应构建所需的配置文件(如.travis.yml、azure-pipelines.yml,或 GitHub Actions 用的crossbow.yml)。CI 服务监听到队列仓库出现新的匹配分支,就会拉取并执行其中的构建。
Scheduler:archery crossbow调度器
调度器负责三件事:版本生成、任务渲染、任务提交。版本号基于setuptools_scm从当前 Git 提交推导;任务定义与任务组来自tasks.yml;渲染通过 Jinja2 模板完成。在 core.py 中可以看到,渲染时使用jinja2.Environment加载模板并以StrictUndefined模式执行——这意味着模板中任何未定义的参数都会直接报错,从而保证"渲染即校验"。
安装 Crossbow:从零到archery crossbow --help
以下步骤以 GitHub 为例(理论上任意 Git 服务器均可)。如果你使用官方的ursacomputing/crossbow队列仓库,可直接跳到第 3 步;否则需要先完成前两步。
1. 创建队列仓库
新建一个 GitHub 仓库作为你的任务队列,例如crossbow。参考仓库创建流程即可(GitHub 的仓库创建向导)。
2. 为队列仓库启用 Azure Pipelines 集成
在 Azure Pipelines 中为新建的队列仓库启用构建集成,这样队列仓库上的分支推送事件才能触发 Azure 上的构建。
3. 克隆 Crossbow 仓库
将官方ursacomputing/crossbow(或你刚创建的仓库)克隆到 arrow 仓库旁边:
git clone https://github.com/<user>/crossbow crossbow默认情况下脚本会在arrow目录旁寻找名为crossbow的克隆,该路径也可以通过命令行参数(--queue-path)覆盖。从 cli.py 可以看到,队列路径默认值就是ArrowSources.find().path.parent / "crossbow",同时支持通过CROSSBOW_QUEUE_PATH环境变量指定。
重要提示:Crossbow 只支持基于 GitHub Token 的认证。虽然代码内部会把 SSH 协议的远程地址重写为 HTTPS(见 core.py 的
_git_ssh_to_https),但文档仍建议直接使用 HTTPS 仓库地址,避免不必要的坑。
4. 创建 Personal Access Token
在 GitHub 上创建一个 Personal Access Token,只需要repo与workflow两个权限(其他权限不需要)。
5. 导出 Token
将 Token 导出为环境变量:
export GH_TOKEN=<token>或者在使用 CLI 时通过--github-token参数传入。此外 cli.py 也支持CROSSBOW_GITHUB_TOKEN环境变量;三个来源的优先级顺序为--github-token>CROSSBOW_GITHUB_TOKEN>GH_TOKEN。如果最终未提供 Token,core.py 的push()会直接抛出RuntimeError提示缺少凭据。
6. 安装 Python
需要 Python 3.11 及以上版本。文档建议优先使用 Miniconda 管理环境(参见 conda 官方安装指南)。
7. 安装 archery(含 Crossbow 子命令)
pip install -e "arrow/dev/archery[crossbow]"[crossbow]extra 会额外引入pygithub、pygit2等 Crossbow 依赖(在 dev/archery/setup.py 中声明)。-e表示可编辑安装,便于跟随仓库开发迭代。
8. 验证安装
archery crossbow --help看到命令帮助即说明安装成功。整个crossbow命令组还提供了子命令级帮助,如archery crossbow submit --help。
核心用法:submit 提交构建任务
基本流程
archery crossbow submit的执行流程(与 cli.py 中submit的实现一一对应):
自动检测当前仓库:脚本检测当前 checkout 的 arrow 仓库及其 remote,因此天然支持 fork。例如在 kszucs 的 fork 下执行,构建的就是 kszucs/arrow 而非上游 apache/arrow:
git clone https://github.com/kszucs/arrow git clone https://github.com/kszucs/crossbow cd arrow/dev/tasks archery crossbow submit --help # 查看可用选项 archery crossbow submit conda-win conda-linux conda-osx读取 HEAD 并生成版本号:脚本取当前 checkout 分支的 HEAD 提交,基于
setuptools_scm推导版本号。因此要构建某个特定分支,请先 checkout 再提交:git checkout ARROW-<ticket number> archery crossbow submit --dry-run conda-linux conda-osx注意:目标分支必须先推送到远程,因为脚本(以及后续 CI 上的克隆)会拉取该分支。
渲染构建配置:读取
tasks.yml中对应的任务定义,用参数替换后渲染出 CI 配置(如crossbow.yml、.travis.yml等)。为每个任务创建分支:按任务创建以 job id 为前缀的分支,例如 Linux 上构建 conda recipes 会创建
crossbow@build-<id>-conda-linux。从源码看,实际分支命名格式为<job.branch>-<task.ci>-<task_name>(core.py),即在任务分支末尾附加 CI 类型与任务名,便于 Travis/CircleCI 使用分支名做跳过匹配。推送分支触发构建:将修改后的分支推送到 GitHub 触发 CI,认证使用上文安装步骤中的 GitHub OAuth Token。
常用参数一览
以下参数来自submit命令的实现(cli.py),可配合--help对照:
| 参数 | 说明 |
|---|---|
tasks(位置参数) | 任务名列表,支持多个,如conda-win conda-linux |
--group, -g | 按tasks.yml中定义的任务组批量提交,可多次指定 |
--param, -p | 附加的任务参数,格式key=value,用于渲染 CI 模板 |
--job-prefix | 分支名的任意前缀,默认build(如 nightly 用--job-prefix nightly) |
--config-path, -c | 任务配置文件 YAML,默认dev/tasks/tasks.yml |
--arrow-version, -v | 显式指定目标版本 |
--arrow-remote, -r | 显式指定要克隆的 GitHub remote(不本地校验),如https://github.com/apache/arrow |
--arrow-branch, -b | 显式指定分支名,如ARROW-1949 |
--arrow-sha, -t | 显式指定提交 SHA 或 Tag,如f67a515、apache-arrow-0.11.1 |
--fetch/--no-fetch | 是否从远程 fetch 引用,默认 fetch |
--dry-run/--commit | 仅渲染展示,不提交(默认 dry-run 为 False,即会提交) |
--no-push/--push | 是否推送变更,--no-push只在本地创建分支和提交 |
任务组机制大大简化了批量调度:例如--group conda只会挑选tasks.yml中conda组列出的任务。完整任务组定义见 dev/tasks/tasks.yml 顶部的groups:段,其中包括wheel、homebrew、packaging、test、cpp、c-glib、python、r、ruby、vcpkg、integration、example、fuzz、verify-rc系列以及nightly、nightly-tests、nightly-packaging、nightly-release等。
示例:多种提交方式
提交多个指定任务:
archery crossbow submit debian-stretch conda-linux-gcc-py37-r40 Repository: https://github.com/kszucs/arrow@tasks Commit SHA: 810a718836bb3a8cefc053055600bdcc440e6702 Version: 0.9.1.dev48+g810a7188.d20180414 Pushed branches: - debian-stretch - conda-linux-gcc-py37-r40仅渲染不提交(dry-run):
archery crossbow submit --dry-run task_name只跑 conda 打包任务加一个 C++ 测试任务:
archery crossbow submit --group conda test-ubuntu-24.04-cpp跑全部 Wheel 构建:
archery crossbow submit --group wheel此外tasks.yml中还有docker、integration、cpp-python等多个任务组,用于运行基于 Docker 的测试矩阵。
任务配置结构速览
在tasks.yml中,每个任务由"任务名 → 定义"组成,其基本结构为:
tasks: # 任意任务名: # template: 指向 jinja2 模板的路径 # params: 可选的额外参数(如镜像名、Python 版本、架构、环境变量) # artifacts: 正则模式列表,每个模式需匹配单个 GitHub release 资产, # 版本变量会替换进模式,例如: # - pyarrow-{no_rc_version}-py38(h[a-z0-9]+)_0-linux-64.tar.bz2以真实任务为例(tasks.yml 中的 manylinux wheel):
wheel-manylinux-2-28-cp311-cp311-amd64: ci: github template: python-wheels/github.linux.yml params: arch: "amd64" linux_wheel_kind: "manylinux" linux_wheel_version: "2-28" python_abi_tag: "cp311" python_version: "3.11" wheel_platform_tag: "manylinux_2_28_x86_64" artifacts: - pyarrow-{no_rc_version}-cp311-cp311-manylinux_2_28_x86_64.whl大量任务通过 Jinja2 的 for 循环批量生成,例如 Python 3.11–3.15 的 Wheel 矩阵、Ubuntu/Debian/Fedora 的 C++ 与 Python 容器测试、Pandas/Dask/Spark/HDFS 集成测试等,维护成本被压缩到极低。
查询构建状态:status 命令
submit会返回一个 build id(对应队列仓库中的一个分支),用它即可查询状态:
archery crossbow status <build id / branch name>实现层面(cli.py),status会 fetch 队列仓库、按 job 名取出任务,再以ConsoleReport逐任务渲染状态。它支持以下选项:
--fetch/--no-fetch:查询前是否 fetch,默认 fetch;--task-filter, -f:Glob 模式过滤关心的任务,可多次指定;--validate/--no-validate:只要存在任一非成功任务就返回非零退出码,便于接入脚本做门禁判断。状态判断依据task.status().combined_state是否为error或failure(cli.py)。
下载构建产物:artifacts 命令
构建完成后,产物会作为资产上传到队列仓库对应的 GitHub release,用以下命令下载:
archery crossbow artifacts <build id / branch name>对应实现是download_artifacts(cli.py),行为要点:
- 默认下载到
<arrow 仓库>/packages/<job-name>,可用-t/--target-dir覆盖; - 支持
--dry-run只展示过程不下载; - 下载前按资产大小与本地文件比对,已存在且大小一致的会跳过;
- 下载失败自动重试(最多 5 次,每次间隔 60 秒);
- 支持
--task-filter过滤任务、--validate-patterns/--skip-pattern-validation控制资产名校验。
报告机制:邮件、聊天与 CSV
Crossbow 还内置了一套报告体系,把构建结果推送到合适的地方,便于夜间构建(nightly)无人值守时自动通知。
邮件报告:report
archery crossbow report <job-name> \ --send \ --sender-name "Arrow CI" --sender-email ci@example.com \ --recipient-email dev@example.com \ --smtp-user ci@example.com --smtp-password <pass> \ --smtp-server smtp.gmail.com --smtp-port 465关键选项(cli.py):--send/--dry-run控制是否真正发送;--poll/--no-poll支持在任务未完成时轮询等待(--poll-max-minutes默认 180 分钟、--poll-interval-minutes默认 10 分钟);邮件主题由NightlyEmailReport生成,形如[NIGHTLY] Arrow Build Report for Job <branch>: N failed, M pending(cli.py)。
聊天报告:report_chat
archery crossbow report_chat <job-name> \ --send --webhook https://hooks.slack.com/... \ --extra-message-success "All green" \ --extra-message-failure "Please investigate"把构建结果以文本形式发送到 Slack / Zulip 等 Webhook 地址;-s/-f两个参数分别指定成功、失败时追加的额外消息。
CSV 报告:report_csv
archery crossbow report_csv <job-name> --save生成 CSV 格式的逐任务报告,便于导入表格工具或做历史分析。
进阶主题:夜间构建、发布验证与队列仓库维护
定时触发夜间构建
Crossbow 支持用 CI 的 cron 能力驱动周期性调度。dev/tasks/nightlies.sample.yml 给出了 Travis cron 的样板:把该文件以.travis.yml的名字放到 crossbow 仓库的某个分支上,配置该分支的每日 cron 任务,然后在script段调用:
if [ $TRAVIS_EVENT_TYPE = "cron" ]; then archery crossbow submit -g conda -g wheel -g linux else archery crossbow submit --dry-run -g conda -g wheel -g linux fi即:cron 触发时真实提交打包任务,手工触发时仅 dry-run 演练。tasks.yml中也预置了nightly、nightly-tests、nightly-packaging、nightly-release等组,配合--job-prefix nightly使用;latest_prefix命令(archery crossbow latest_prefix nightly)可快速取得某个前缀的最新 job。
发布候选验证:verify_release_candidate
Crossbow 深度参与 Apache Arrow 的发布流程。verify_release_candidate命令(cli.py)会自动创建(或查找)一个 PR,并通过在 PR 上添加@github-actions crossbow submit --group verify-rc-xxx --param release=<version> --param rc=<rc>形式的评论来触发验证任务:
archery crossbow verify_release_candidate \ --version 9.0.0 --rc 0 \ --verify-source --verify-binaries --verify-wheels \ --create-pr --head-branch release-9.0.0-rc0verify-rc-source、verify-rc-binaries、verify-rc-wheels三组任务在 tasks.yml 中展开为覆盖 Linux(conda/almalinux/ubuntu 各发行版)、macOS(Intel/arm64)与 Windows 的验证矩阵,使用 verify-rc 目录下的模板执行。
队列仓库维护
队列仓库长期使用后分支会越积越多,Crossbow 提供清理命令:
archery crossbow delete_old_branches --dry-run --days 90 --maximum 1000它删除超过指定天数(默认 90 天)的旧分支,每次最多删除--maximum(默认 1000)个,且跳过origin/pr/*引用——这是为了避免触发 GitHub 的引用保护限制(cli.py)。
Token 过期提醒
GitHub Token 有有效期,Crossbow 提供notify_token_expiration命令,在 Token 距过期不足指定天数(默认 30 天)时发送提醒邮件,主题形如[CI] Arrow Crossbow Token Expiration in <date>(cli.py)。
底层机制剖析:从提交到分支推送
理解 Crossbow 的调度本质,关键在于 core.py 中的几个核心类:
Repo:对本地 Git 仓库的高层封装,既用于读取 arrow 仓库的版本信息(head、branch、remote_url),也用于向队列仓库推送任务(core.py)。它强制 HTTPS:如果 origin 是git@github.com形式会抛错,并要求提供 GitHub Token(CROSSBOW_GITHUB_TOKEN或GH_TOKEN)。Queue(Repo):队列仓库专用类,负责 job 分支的自动编号(build-85这类自增 id,或build-41d017af40这类随机 hex id)、按名取 job、按模式枚举 job、查询某个前缀的最新 job(core.py)。job 元数据以job.yml形式存在分支提交里,Queue.get()读取并反序列化该文件。Target:描述"构建哪个版本",可由当前仓库自动推导,也可通过--arrow-version/--arrow-remote/--arrow-branch/--arrow-sha覆盖,这正是发布流程需要精确锁定某个 RC 提交时的关键设计。Job/Task:job 是一次提交的集合(一个 build id 对应一个 job),task 是其中某个具体任务;Job.from_config根据tasks.yml与命令行参数展开任务集,渲染模板后为每个任务生成独立分支并加入推送列表。
一次典型的提交链路为:Target.from_repo()确定目标 →Job.from_config()加载并展开任务 →queue.put(job)在本地队列仓库创建各任务分支与提交 →queue.push()用 OAuth Token 推送到远程 → 各 CI 服务按分支名匹配并执行 → 结果回传到队列仓库 →status/artifacts/report*命令消费结果。分支创建时以默认分支(main)的提交为父提交(core.py),这样能复用 GitHub Actions 的缓存。
小结
Crossbow 用一套极简而优雅的思路解决了多语言、多平台、多任务类型下的打包与集成测试调度问题:Git 分支即任务队列,CI 服务天然按分支触发;tasks.yml+ Jinja2 模板把成百上千的构建矩阵压缩为可维护的声明式配置;archery crossbow系列命令则覆盖了从提交、查询、下载到报告、清理、发布验证的完整生命周期。无论你是想复现 Arrow 的某次构建、为 fork 跑自己的打包任务,还是在自己的项目里借鉴这种"以 Git 分支做 CI 队列"的调度模式,crossbow.rst 连同 dev/tasks/tasks.yml、dev/archery/archery/crossbow/cli.py 与 core.py 都是最佳的起点。
【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考